二分图'最大匹配' HK 算法

重要的事->//直接摘抄了博客中的内容。->原博客点我

求二分图最大匹配——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\n");
61         else printf("NO\n");
62     }
63     return 0;
64 }
View Code

// 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\n", HK() == p ? "YES" : "NO");
102     }
103     return 0;
104 }

 

posted @ 2019-11-03 18:15  pupil337  阅读(1412)  评论(3编辑  收藏  举报