最近在学习二分图相关算法,以下内容是根据自己的理解,欢迎讨论。
什么是二分图:
二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。
二分图的最大匹配:
给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
求二分图最大匹配可以用最大流或者匈牙利算法。
最大流求最大二分匹配的算法中,所有边容量为1,增加额外两个节点是,s,t,将s与A集中的顶点相连,t与B集中顶点相连,运行一遍最大流算法即可。以下主要讨论匈牙利算法。
先给出匈牙利算法代码,以hdu1150为例 http://acm.hdu.edu.cn/showproblem.php?pid=1150
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int map[105][105]; //map[i][j]==1;表示A集中i与B集j有联通 int vis[105]; //B集中的访问标记。 int link1[105],n,m,k; //link1[i]表示B集i匹配的A集中的元素,若link1[i]==-1,则表示i目前没有匹配 bool can(int i) //寻找A集i的匹配 { int j; for(j=0;j<m;j++) { if(!vis[j]&&map[i][j]) //虽然j从0-m扫描,不可能重复访问,但vis数组还是有必要的,原因在于下面的can(link1[j]) { //有了vis数组后can(link1[j])就不可以再找到j。 vis[j]=1; if(link1[j]==-1||can(link1[j])) //若B中j没有匹配或与j匹配的A中的元素有其他匹配 { link1[j]=i; return true; } } } return false; } int main() { while(scanf("%d",&n),n!=0) { scanf("%d%d",&m,&k); int i; int z; memset(map,0,sizeof(map)); for(i=0;i<k;i++) { int t1,t2; scanf("%d%d%d",&z,&t1,&t2); if(t1>0&&t2>0) map[t1][t2]=1; } int s=0; memset(link1,-1,sizeof(link1)); for(i=0;i<n;i++) { memset(vis,0,sizeof(vis)); if(can(i)) s++; } printf("%d\n",s); } }
1的证明可以用反证法,若最大匹配数M(最小点数)不能覆盖所有边,则存在额外的边e,把e加入可以得到更大的M。矛盾。
最小路径覆盖数:
一个A个结点的有向无环图中,路径覆盖就是在图中找一些路径,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联;(如果把这些路径中的每条路径从它的起始点走到它的终点,那么恰好可以经过图中的每个顶点一次且仅一次)。
路径覆盖与二分图匹配的关系(必须是没有圈的有向图):最小路径覆盖=|A|-分拆后最大匹配数
将A中i结点分拆一个B中i‘,则有限边中i->j 对应i->j' 原图的结点数-A与B的最大匹配数为最小路径覆盖。
证明根据自己的理解:首先将每个点看成一个路径即p->p 则覆盖所有点需要n个路径,在AB匹配中若对于i->j'则原图有i->j;这样选取此边可是路径数-1,因为在AB匹配中一个点对于一条边,所以在原图中一个点也对应一个边,即满足路径覆盖。。所以 原图的结点数-A与B的最大匹配数为最小路径覆盖。