重要的事->//直接摘抄了博客中的内容。->原博客点我
求二分图最大匹配——Hopcroft-Krap算法
本文是对二分图大讲堂这篇文章中Hopcroft-Krap算法代码实现的详细注释。
下面一段话来自上面一博客* 该段话很重要orz(ToT)/~~~
Hopcroft-Karp算法实现
下面的实现有详细的注释,该算法还是不完美,每次调用searchP()值保留了一个最小的dis值(为什么是最小,因为其是BFS遍历,当同一层次有一个v满足My[v]==-1时,dis就附上相应的层次值),也就是在长度大于dis的层在本次调用时再遍历下去,只能是下次调用searchP()查找,花了好几个小时去理解。
通过上面的分析,易知searchP()是没有遍历层次大于dis的层,也就是说没有把长度大于dis增广路径是没有找到的。当然这样做的好处——防止出现相交的增广路径。
还有个要知道的是dis在下面这个算法中的值只可能是从1逐渐增加偶数变大的,所以这样做是不可能在一次searchP()调用之后DFS出现相交的增广路径的(一定只会是长度小的那个增广路径)。
HK算法的基本原理
Hopcroft-Karp算法先使用BFS查找多条增广路,然后使用DFS遍历增广路(累加匹配数,修改匹配点集),循环执行,直到没有增广路为止。
Hopcroft-Karp算法的BFS遍历只对点进行分层(不标记是匹配点和未匹配点),然后用DFS遍历看上面的层次哪些是增广路径(最后一个点是未匹配的)。
BFS过程可以看做是图像树结构一样逐层向下遍历,还要防止出现相交的增广路径。
二分图大讲堂中给出了HK算法步骤的通俗解释
设U和V是图G的二分图,M是从U到V的匹配
(1)使用BFS遍历对图的点进行分层,从X中找出一个未匹配点v,(所有v)组成第一层,接下的层是这样形成的——都是查找匹配点(增广路性质),直到在V中找到未匹配点才终止查找,对X其他未匹配点同样进行查找增广路径(BFS只分层不标记是否匹配点)
(2)使用DFS遍历查找(1)形成的增广路,找到就匹配数就累加1
(3)重复(1)(2)操作直到找不出增广路径为止
而二分图最大匹配之Hopcroft-Karp算法里给了比较学术化的步骤,有兴趣可以看一下
// 匈牙利算法时间复杂度O(V*E) HK复杂度O(sqrt(V)*E)
// 附匈牙利代码
1 #include<cstdio> 2 using namespace std; 3 const int MAXN = 1000+5; 4 const int MAXM = 3000+5; 5 6 int num; 7 int head[MAXN]; 8 struct node { 9 int v, next; 10 } edge[MAXN*MAXM]; 11 12 inline void add(int x, int y) { 13 edge[num].v = y; 14 edge[num].next = head[x]; 15 head[x] = num++; 16 } 17 18 int n, m; 19 int nm[MAXN]; 20 21 int love[MAXM]; 22 bool vis[MAXM]; 23 bool dfs(int u) { 24 for(int i = head[u]; i != -1; i = edge[i].next) { 25 int v = edge[i].v; 26 if(!vis[v]) { 27 vis[v] = true; 28 if(!love[v] || dfs(love[v])) { 29 love[v] = u; 30 return true; 31 } 32 } 33 } 34 return false; 35 } 36 37 bool solve() { 38 for(int i = 1; i <= m; ++i) love[i] = 0; 39 for(int i = 1; i <= n; ++i) { 40 for(int j = 1; j <= m; ++j) vis[j] = false; 41 if(nm[i] && !dfs(i)) return false; 42 } 43 return true; 44 } 45 46 int main() { 47 int T; 48 scanf("%d", &T); 49 while(T--) { 50 scanf("%d%d", &n, &m); 51 for(int i = 1; i <= n; ++i) head[i] = -1; 52 int p; 53 for(int i = 1; i <= n; ++i) { 54 scanf("%d", &nm[i]); 55 for(int j = 0; j != nm[i]; ++j) { 56 scanf("%d", &p); 57 add(i, p); 58 } 59 } 60 if(solve()) printf("YES "); 61 else printf("NO "); 62 } 63 return 0; 64 }
// hdu 1083 HK算法(非典型例题 匈牙利也能做)
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #define INF 0x3f3f3f3f 5 using namespace std; 6 7 const int MAXN = 100+5; 8 const int MAXM = 300+5; 9 10 int p, n; 11 int a[MAXN][MAXM]; 12 13 int dis; 14 int cx[MAXN], cy[MAXM]; 15 int dx[MAXN], dy[MAXM]; 16 bool vis[MAXM]; 17 18 bool bfs_findPath() { 19 queue<int> q; 20 memset(dx, -1, sizeof(dx)); 21 memset(dy, -1, sizeof(dy)); 22 // 使用BFS遍历对图的点进行分层,从X中找出一个未匹配点v 23 // (所有v)组成第一层,接下来的层都是这样形成——每次查找 24 // 匹配点(增广路性质),直到在Y中找到未匹配点才停止查找, 25 // 对X其他未匹配点同样进行查找增广路径(BFS只分层不标记 26 // 是否匹配点) 27 // 找出X中的所有未匹配点组成BFS的第一层 28 dis = INF; 29 for(int i = 1; i <= p; ++i) { 30 if(cx[i] == -1) { 31 q.push(i); 32 dx[i] = 0; 33 } 34 } 35 while(!q.empty()) { 36 int u = q.front(); 37 q.pop(); 38 if(dx[u] > dis) break;// 该路径长度大于dis,等待下一次BFS扩充 39 for(int v = 1; v <= n; ++v) { 40 if(a[u][v] && dy[v] == -1) {// (u,v)之间有边且v还没有分层 41 dy[v] = dx[u] + 1; 42 if(cy[v] == -1) dis = dy[v];// v是未匹配点,停止延伸(查找),得到本次BFS的最大遍历层次 43 else {// v是已匹配点,继续延伸 44 dx[cy[v]] = dy[v] + 1; 45 q.push(cy[v]); 46 } 47 } 48 } 49 } 50 return dis != INF;// 若dis为INF说明Y中没有未匹配点,也就是没有增广路径了 51 } 52 53 bool dfs(int u) { 54 for(int v = 1; v <= n; ++v) { 55 if(!vis[v] && a[u][v] && dy[v] == dx[u] + 1) { 56 vis[v] = 1; 57 // 层次(也就是增广路径的长度)大于本次查找的dis 58 // 是bfs中被break的情况,也就是还不确定是否是增广路 59 // 只有等再次调用bfs再判断(每次只找最小增广路集) 60 if(cy[v] != -1 && dy[v] == dis) continue; 61 if(cy[v] == -1 || dfs(cy[v])) {// 是增广路径,更新匹配集 62 cy[v] = u; 63 cx[u] = v; 64 return true; 65 } 66 } 67 } 68 return false; 69 } 70 71 int HK() { 72 int ans = 0; 73 memset(cx, -1, sizeof(cx)); 74 memset(cy, -1, sizeof(cy)); 75 while(bfs_findPath()) {// 有增广路 76 memset(vis, 0, sizeof(vis)); 77 for(int i = 1; i <= p; ++i) { 78 // 用DFS查找增广路径,增广路径一定从未匹配点开始 79 // 如果查找到一个增广路径,匹配数加一 80 if(cx[i] == -1 && dfs(i)) ++ans; 81 } 82 } 83 return ans; 84 } 85 86 int main() { 87 int T; 88 scanf("%d", &T); 89 while(T--) { 90 scanf("%d%d", &p, &n); 91 memset(a, 0, sizeof(a)); 92 93 for(int i = 1; i <= p; ++i) { 94 int nm, x; 95 scanf("%d", &nm); 96 for(int j = 0; j != nm; ++j) { 97 scanf("%d", &x); 98 a[i][x] = 1; 99 } 100 } 101 printf("%s ", HK() == p ? "YES" : "NO"); 102 } 103 return 0; 104 }