二分图最大匹配的匈牙利算法:
二分图是这样一个图,它的顶点能够分类两个集合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在实际实现中更加简短。
算法思想:
算法的思路是不停的找增广轨, 并添加匹配的个数,增广轨顾名思义是指一条能够使匹配数变多的路径,在匹配问题中,增广轨的表现形式是一条"交错轨",也就是说这条由图的边组成的路径, 它的第一条边是眼下还没有參与匹配的,第二条边參与了匹配,第三条边没有..最后一条边没有參与匹配,而且始点和终点还没有被选择过.这样交错进行,显然 他有奇数条边.那么对于这样一条路径,我们能够将第一条边改为已匹配,第二条边改为未匹配...以此类推.也就是将全部的边进行"反色",easy发现这样修 改以后,匹配仍然是合法的,可是匹配数添加了一对.另外,单独的一条连接两个未匹配点的边显然也是交错轨.能够证明,当不能再找到增广轨时,就得到了一个 最大匹配.这也就是匈牙利算法的思路.、
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是有几组数据关系。。