|
二分图最大匹配的匈牙利算法: 二分图是这样一个图,它的顶点可以分类两个集合X和Y,所有的边关联在两个顶点中,恰好一个属于集合X,另一个属于集合Y。 最大匹配: 图中包含边数最多的匹配称为图的最大匹配。 完美匹配: 如果所有点都在匹配边上,称这个最大匹配是完美匹配。 最小覆盖: 最小覆盖要求用最少的点(X集合或Y集合的都行)让每条边都至少和其中一个点关联。可以证明:最少的点(即覆盖数)=最大匹配数 最小路径覆盖: 用尽量少的不相交简单路径覆盖有向无环图G的所有结点。解决此类问题可以建立一个二分图模型。把所有顶点i拆成两个:X结点集中的i和Y结点集中的i',如果有边i->j,则在二分图中引入边i->j',设二分图最大匹配为m,则结果就是n-m。
二分图最大匹配的经典匈牙利算法是由Edmonds在1965年提出的,算法的核心就是根据一个初始匹配不停的找增广路,直到没有增广路为止。 匈牙利算法的本质实际上和基于增广路特性的最大流算法还是相似的,只需要注意两点: (一)每个X节点都最多做一次增广路的起点; (二)如果一个Y节点已经匹配了,那么增广路到这儿的时候唯一的路径是走到Y节点的匹配点(可以回忆最大流算法中的后向边,这个时候后向边是可以增流的)。 找增广路的时候既可以采用dfs也可以采用bfs,两者都可以保证O(nm)的复杂度,因为每找一条增广路的复杂度是O(m),而最多增广n次,dfs在实际实现中更加简短。
算法思想: 算 法的思路是不停的找增广轨, 并增加匹配的个数,增广轨顾名思义是指一条可以使匹配数变多的路径,在匹配问题中,增广轨的表现形式是一条"交错轨",也就 是说这条由图的边组成的路径, 它的第一条边是目前还没有参与匹配的,第二条边参与了匹配,第三条边没有..最后一条边没有参与匹配,并且始点和终点还没 有被选择过.这样交错进行,显然 他有奇数条边.那么对于这样一条路径,我们可以将第一条边改为已匹配,第二条边改为未匹配...以此类推.也就是将所有 的边进行"反色",容易发现这样修 改以后,匹配仍然是合法的,但是匹配数增加了一对.另外,单独的一条连接两个未匹配点的边显然也是交错轨.可以证明, 当不能再找到增广轨时,就得到了一个 最大匹配.这也就是匈牙利算法的思路.、 C邻接矩阵: #include<stdio.h> #include<string.h> #include<math.h> int result[105]; int state[105]; int data[105][105]; int n1,n2,m,ans; int init() { int i,x,y; memset(result,0,sizeof(result)); memset(data,0,sizeof(data)); scanf("%d%d%d",&n1,&n2,&m); for (i=0;i<m ;i++ ) { scanf("%d%d",&x,&y); data[x][y]=1; } return 0; } int find(int x) { int i; for (i=1;i<=n2 ;i++ ) { if (data[x][i]==1 && !state[i]) { state[i]=1; if (result[i]==0 || find(result[i])) { result[i]=x; return 1; } } } return 0; } int main() { int i; init(); ans=0; for (i=1;i<=n1 ;i++ ) { memset(state,0,sizeof(state)); if (find(i)) ans++; } printf("%d\n",ans); return 0; }
POJ_1274: #include<stdio.h> #include<string.h> #include<math.h> int n,m,ans; int link[205][205]; int state[205]; int result[205]; int find(int x) { int i; for (i=1;i<=m ;i++ ) { if (link[x][i] && !state[i]) { state[i]=1; if (result[i]==0 || find(result[i])) { result[i]=x; return 1; } } } return 0; } int main() { int i,j,Si,st; while (scanf("%d%d",&n,&m)==2) { memset(link,0,sizeof(link)); memset(result,0,sizeof(result)); for (i=1;i<=n ;i++ ) { scanf("%d",&Si); for (j=1;j<=Si ;j++ ) { scanf("%d",&st); link[i][st]=1; } } ans=0; for (i=1;i<=n ;i++ ) { memset(state,0,sizeof(state)); if (find(i)) ans++; } printf("%d\n",ans); } return 0; }
|