SPOJ 4206 Fast Maximum Matching (二分图最大匹配 Hopcroft-Carp 算法 模板)
题目大意:
有n1头公牛和n2头母牛,给出公母之间的m对配对关系,求最大匹配数。数据范围: 1 <= n1, n2 <= 50000, m <= 150000
算法讨论:
第一反应KM直接上,第二反应,KM是O(N^2 * M)的,会T成狗。
第二反应,看看大家是怎么做的。后来发现了一个名字叫 Hopcroft-Carp的二分图最大匹配的算法。可以在O(sqrt(n) * m)的时间内解决二分图的最大匹配问题。非常适合大数据的二分图匹配。所以就学习了一下。 我们知道,普通的匈牙利慢的原因是因为他一次深搜只找一条增广路,而HC算法是利用Bfs同时找多条增广路。
下面有网友提供的HC算法的原理:
SRbGa很早就介绍过这个算法,它可以做到O(sqrt(n)*e)的时间复杂度,并且在实际使用中效果不错而且算法本身并不复杂。
Hopcroft-Karp算法是Hopcroft和Karp在1972年提出的,该算法的主要思想是在每次增广的时候不是找一条增广路而是同时找几条不相交的最短增广路,形成极大增广路集,随后可以沿着这几条增广路同时进行增广。
可以证明在寻找增广路集的每一个阶段所寻找到的最短增广路都具有相等的长度,并且随着算法的进行最短增广路的长度是越来越长的,更进一步的分析可以证明最多只需要增广ceil(sqrt(n))次就可以得到最大匹配(证明在这里略去)。
因此现在的主要难度就是在O(e)的时间复杂度内找到极大最短增广路集,思路并不复杂,首先从所有X的未盖点进行BFS,BFS之后对每个X节点和Y节点 维护距离标号,如果Y节点是未盖点那么就找到了一条最短增广路,BFS完之后就找到了最短增广路集,随后可以直接用DFS对所有允许弧 (dist[y]=dist[x]+1,可以参见高流推进HLPP的实现)进行类似于匈牙利中寻找增广路的操作,这样就可以做到O(m)的复杂度。
实现起来也并不复杂,对于两边各50000个点,200000条边的二分图最大匹配可以在1s内出解~~
那么此题就可以用HC算法轻松跑过。提醒,一定要注意常数。SPOJ跑得太慢了。
果然我的常数是很大。。。。。。
Codes:
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 8 struct Hopcroft_Carp{ 9 static const int N = 50000 + 5; 10 static const int M = 150000 + 5; 11 static const int oo = 0x3f3f3f3f; 12 13 int n1, n2, res, tot; 14 int dsx[N], dsy[N], mx[N], my[N]; 15 int que[N<<1];bool vi[N<<1]; 16 int first[N<<1], next[M]; 17 int u[M], v[M]; 18 19 void Clear(){tot = 0; res = 0;} 20 void Add(int from, int to){ 21 ++ tot; 22 u[tot] = from; v[tot] = to; 23 next[tot] = first[u[tot]]; 24 first[u[tot]] = tot; 25 } 26 27 bool bfs(){ 28 for(int i = 1; i <= n1; ++ i) dsx[i] = -1; 29 for(int i = 1; i <= n2; ++ i) dsy[i] = -1; 30 res = oo; 31 32 int head, tail; 33 head = tail = 1; 34 for(int i = 1; i <= n1; ++ i) 35 if(mx[i] == -1) que[++ tail] = i, dsx[i] = 0; 36 while(head <= tail){ 37 int x = que[head]; 38 if(dsx[x] > res) break; 39 40 for(int i = first[x]; i; i = next[i]){ 41 if(dsy[v[i]] == -1){ 42 dsy[v[i]] = dsx[x] + 1; 43 if(my[v[i]] == -1) res = dsy[v[i]]; 44 else{ 45 dsx[my[v[i]]] = dsy[v[i]] + 1; 46 que[++ tail] = my[v[i]]; 47 } 48 } 49 } 50 ++ head; 51 } 52 return res != oo; 53 } 54 55 bool dfs(int x){ 56 for(int i = first[x]; i; i = next[i]){ 57 if(!vi[v[i]] && dsy[v[i]] == dsx[x] + 1){ 58 vi[v[i]] = true; 59 if(my[v[i]] != -1 && dsy[v[i]] == res) continue; 60 if(my[v[i]] == -1 || dfs(my[v[i]])){ 61 my[v[i]] = x; 62 mx[x] = v[i]; 63 return true; 64 } 65 } 66 } 67 return false; 68 } 69 70 int MaxMatch(){ 71 int ans = 0; 72 for(int i = 1; i <= n1; ++ i) mx[i] = -1; 73 for(int i = 1; i <= n2; ++ i) my[i] = -1; 74 75 while(bfs()){ 76 for(int i = 1; i <= n1+n2; ++ i) vi[i] = false; 77 for(int i = 1; i <= n1; ++ i) 78 if(mx[i] == -1 && dfs(i)) ans ++; 79 } 80 return ans; 81 } 82 }Two; 83 84 int n1, n2, m; 85 86 int main(){ 87 int x, y; 88 scanf("%d%d%d", &n1, &n2, &m); 89 Two.Clear(); 90 Two.n1 = n1; Two.n2 = n2; 91 for(int i = 1; i <= m; ++ i){ 92 scanf("%d%d", &x, &y); 93 Two.Add(x, y); 94 } 95 printf("%d\n", Two.MaxMatch()); 96 return 0; 97 }