• 二分图'最大匹配' 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
    ");
    61         else printf("NO
    ");
    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
    ", HK() == p ? "YES" : "NO");
    102     }
    103     return 0;
    104 }
  • 相关阅读:
    php array_flip() 删除数组重复元素——大彻大悟
    深入理解HTTP协议之POST方法——ajax实例
    RHEL/CentOS/Fedora各种源(EPEL、Remi、RPMForge、RPMFusion)配置
    SpringMVC注解校验
    Mybatis源码解析(二)
    MyBatist庖丁解牛(三)
    MyBatist庖丁解牛(二)
    linux模拟http请求命令
    清空文件内容
    git上如何删除已有项目
  • 原文地址:https://www.cnblogs.com/pupil-xj/p/11788332.html
Copyright © 2020-2023  润新知