二分图最大匹配的匈牙利算法:
二分图是这样一个图,它的顶点可以分类两个集合X和Y,所有的边关联在两个顶点中,恰好一个属于集合X,另一个属于集合Y。
最大匹配: 图中包含边数最多的匹配称为图的最大匹配。
完美匹配: 如果所有点都在匹配边上,称这个最大匹配是完美匹配。
最小覆盖: 最小覆盖要求用最少的点(X集合或Y集合的都行)让每条边都至少和其中一个点关联。可以证明:最少的点(即覆盖数)=最大匹配数
最小路径覆盖:
用尽量少的不相交简单路径覆盖有向无环图G的所有结点。解决此类问题可以建立一个二分图模型。把所有顶点i拆成两个:X结点集中的i和Y结点集中的i',如果有边i->j,则在二分图中引入边i->j',设二分图最大匹配为m,则结果就是n-m。
最小路径覆盖问题值得注意的地方
首先,最小路径覆盖=总节点数-最大匹配数。这个应该已经是路人皆知了。
所谓最小路径覆盖,是指在一个有向图中,找出最少的几条路径,用它们来覆盖全图
这里说的值得注意的地方,如果有向图的边有相交的情况,那么就不能简单的对原图求二分匹配了
举个例子,假设有图:1->2 2->5 2->3 4->2,事实上,这其实就是两条边:1->5 4->3 ,节点2只是他们的一个交点
如果只是简单的在原图的基础上求二分匹配,那么得到的匹配答案是2,最小路径覆盖答案便是5-2=3。
可是随便一看都能看看出端倪,这个图中,只需要两个点便可以探索完整个地图,这里最小路径覆盖数明显是2。
问题究竟出在哪里呢?其实就和这个交点2有关。既然边有相交,那么他们的连通性也应该连通下去。
解决的办法是对原图进行一次闭包传递(也就是flody),于是便增加了四条边:1->3 1->5 4->3 4->5
这时再求最大匹配数,匹配答案便是3,最小路径覆盖值为2,这是正确答案!
具体问题可见 PKU 2594 Treasure Exploration
无向二分图的最小路径覆盖 = 顶点数 – 最大二分匹配数/2
二分图最大匹配的经典匈牙利算法是由Edmonds在1965年提出的,算法的核心就是根据一个初始匹配不停的找增广路,直到没有增广路为止。
匈牙利算法的本质实际上和基于增广路特性的最大流算法还是相似的,只需要注意两点:
(一)每个X节点都最多做一次增广路的起点;
(二)如果一个Y节点已经匹配了,那么增广路到这儿的时候唯一的路径是走到Y节点的匹配点(可以回忆最大流算法中的后向边,这个时候后向边是可以增流的)。
找增广路的时候既可以采用dfs也可以采用bfs,两者都可以保证O(nm)的复杂度,因为每找一条增广路的复杂度是O(m),而最多增广n次,dfs在实际实现中更加简短。
算法思想:
算法的思路是不停的找增广轨, 并增加匹配的个数,增广轨顾名思义是指一条可以使匹配数变多的路径,在匹配问题中,增广轨的表现形式是一条"交错轨",也就是说这条由图的边组成的路径, 它的第一条边是目前还没有参与匹配的,第二条边参与了匹配,第三条边没有..最后一条边没有参与匹配,并且始点和终点还没有被选择过.这样交错进行,显然 他有奇数条边.那么对于这样一条路径,我们可以将第一条边改为已匹配,第二条边改为未匹配...以此类推.也就是将所有的边进行"反色",容易发现这样修 改以后,匹配仍然是合法的,但是匹配数增加了一对.另外,单独的一条连接两个未匹配点的边显然也是交错轨.可以证明,当不能再找到增广轨时,就得到了一个 最大匹配.这也就是匈牙利算法的思路.、
Code:
2 * Problem: 图的匹配
3 * Author: Xu Fei
4 * Time: 2010.8.5 11:43
5 * Method: hungary 匈牙利算法
6 */
7 #include < iostream >
8 #include < cstdio >
9 #include < cstring >
10 using namespace std;
11
12 const int MaxN = 100 ;
13
14 int N,M;
15 int Ans;
16 int link[MaxN];
17 bool cover[MaxN];
18 bool Map[MaxN][MaxN];
19
20 void init()
21 {
22 int i,x,y;
23 scanf( " %d%d " , & N, & M);
24 memset(Map, false , sizeof (Map));
25 for (i = 1 ;i <= M; ++ i)
26 {
27 scanf( " %d%d " , & x, & y);
28 Map[x][y] = true ;
29 }
30 }
31 bool Find( int i)
32 {
33 int j;
34 for (j = 1 ;j <= N; ++ j)
35 if (Map[i][j] && ! cover[j])
36 {
37 cover[j] = true ;
38 if ( ! link[j] || Find(link[j]))
39 {
40 link[j] = i;
41 return true ;
42 }
43 }
44 return false ;
45 }
46 void solve()
47 {
48 int i;
49 memset(link, 0 , sizeof (link));
50 for (i = 1 ;i <= N; ++ i)
51 {
52 memset(cover, false , sizeof (cover));
53 Find(i);
54 }
55 }
56 void out ()
57 {
58 int i;
59 Ans = 0 ;
60 for (i = 1 ;i <= N; ++ i)
61 if (link[i])
62 Ans ++ ;
63 printf( " %d\n " ,Ans);
64 for (i = 1 ;i <= N; ++ i)
65 if (link[i])
66 printf( " %d %d\n " ,link[i],i);
67 }
68 int main()
69 {
70 init();
71 solve();
72 out ();
73 return 0 ;
74 }
N是有几组x, y;
M是有几组数据关系。。