zoukankan      html  css  js  c++  java
  • 二分图的最大匹配以及带权匹配【匈牙利算法+KM算法】

    二分图算法包括 匈牙利算法 与 KM算法。

    匈牙利算法

    在这里写上模板。

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2063

     1 #include<stdio.h>
     2 #include<string.h>
     3 #define mem(a, b) memset(a, b, sizeof(a))
     4 
     5 int head[510], cnt;
     6 int k, m, n; //k为组合数,m为女生人数,n为男生人数 
     7 int used[510], master[510];
     8 
     9 struct Edge
    10 {
    11     int to, next;
    12 }edge[1010];
    13 
    14 void add(int a, int b)
    15 {
    16     edge[++ cnt].to = b;
    17     edge[cnt].next = head[a];
    18     head[a] = cnt;
    19 }
    20 
    21 int find(int x)
    22 {
    23     for(int i = head[x]; i != -1; i = edge[i].next)
    24     {
    25         int to = edge[i].to;
    26         if(used[to] == -1)
    27         {
    28             used[to] = 1;
    29             if(master[to] == -1 || find(master[to]))
    30             {
    31                 master[to] = x;
    32                 return 1;
    33             }
    34         }
    35     }
    36     return 0;
    37 }
    38 
    39 int main()
    40 {
    41     int ans;
    42     while(scanf("%d", &k)!=EOF)
    43     {
    44         if(k == 0)
    45             break;
    46         cnt = ans = 0;
    47         mem(head, -1), mem(master, -1);
    48         scanf("%d%d", &m, &n);
    49         for(int i = 1; i <= k; i ++)
    50         {
    51             int a, b;
    52             scanf("%d%d", &a, &b);
    53             add(a, b);
    54         }
    55         for(int i = 1; i <= m; i ++)
    56         {
    57             mem(used, -1);
    58             if(find(i))
    59                 ans ++;
    60         }
    61         printf("%d
    ", ans);
    62     }
    63     return 0;
    64 }
    View Code

     KM算法

    KM算法是用来解决带权问题的最大匹配. (用邻接矩阵实现, 首先因为带权的话, X部,Y部都有边,一般是稠密图,其次邻接表并不好实现)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2255

     1 #include<stdio.h>
     2 #include<string.h>
     3 #define mem(a, b) memset(a, b, sizeof(a))
     4 const int inf = 0x3f3f3f3f;
     5 
     6 int n, nx, ny;
     7 int lx[310], ly[310];//x部点的值,y部点的值 
     8 int visx[310], visy[310];//标记x,y部的点是否在相等子图中,用于更新点的值 
     9 int slack[310];//松弛量, 用于优化KM算法 优化后复杂度为 n^3 
    10 int goal[310], weight[310][310];
    11 
    12 int find(int x)//新增的x部的点 
    13 {
    14     visx[x] = 1;
    15     for(int j = 1; j <= ny; j ++)
    16     {
    17         if(!visy[j])
    18         {
    19             int t = lx[x] + ly[j] - weight[x][j]; //匹配到的标准是 x部 + y部点的值等于边权值 
    20             if(t == 0)
    21             {
    22                 visy[j] = 1;
    23                 if(goal[j] == -1 || find(goal[j]))
    24                 {
    25                     goal[j] = x;
    26                     return 1;
    27                 }
    28             }
    29             else if(slack[j] > t)//没被匹配到的点记录最小slack 
    30                 slack[j] = t;
    31         }
    32     }
    33     return 0;
    34 }
    35 
    36 int km()
    37 {
    38     mem(ly, 0); //y部的初始化为0
    39     mem(lx, 0);
    40     mem(goal, -1);
    41     for(int i = 1; i <= nx; i ++)//x部点的值初始化为与y部相连的最大值 
    42         for(int j = 1; j <= ny; j ++)
    43             if(weight[i][j] > lx[i])
    44                 lx[i] = weight[i][j];
    45     for(int i = 1; i <= nx; i ++)
    46     {//每次扩充一个点, 都要重新初始化y部的slack,因为需要在相等子图中找到最大的权值匹配 
    47         for(int j = 1; j <= ny; j ++)
    48             slack[j] = inf;
    49         while(1)
    50         {
    51             mem(visx, 0);
    52             mem(visy, 0);
    53             if(find(i))  //如果当前子图可以匹配的到就跳出, 扩充下一个x部的点继续匹配 
    54                 break;
    55             //如果当前子图没匹配到,就用slack更新值再循环while寻找当前子图的最大权值匹配 
    56             int d = inf;
    57             for(int j = 1; j <= ny; j ++)
    58                 if(!visy[j] && d > slack[j])
    59                     d = slack[j];//找到一个最小的差值 在未尝试匹配的y部中找 
    60             for(int j = 1; j <= ny; j ++)
    61                 if(!visy[j])
    62                     slack[j] -= d;
    63             for(int j = 1; j <= n; j ++)//参与匹配的点x部的减 ,y部的加 
    64             {
    65                 if(visy[j])
    66                     ly[j] += d;
    67                 if(visx[j])    
    68                     lx[j] -= d;
    69             }            
    70         }
    71     }
    72     int ans = 0;
    73     for(int j = 1; j <= ny; j ++)
    74         if(goal[j] != -1)
    75             ans += weight[goal[j]][j];
    76     return ans;
    77 }
    78 
    79 int main()
    80 {
    81     while(scanf("%d", &n)!=EOF)
    82     {
    83         nx = n, ny = n;
    84         for(int i = 1; i <= n; i ++)
    85             for(int j = 1; j <= n; j ++)
    86                 scanf("%d", &weight[i][j]);
    87         int ans = km();
    88         printf("%d
    ", ans);
    89     }
    90     return 0;
    91 }
    View Code

    对于KM算法求最小匹配, 只需要在最大匹配的模板上改动几个地方即可,

    在存图时将边权全记为负边权,那么会发现在对lx顶标记录最大值的时候实际上是绝对值最小的负值,也就是最小匹配了. 将lx[]数组初始化为-inf,然后对于最后的答案取负号就可以了.

  • 相关阅读:
    jmetal随机数
    [转]IDEA断点调试基础
    [转]java指数表示最大数和最小数
    反向学习相对基学习opposition-based learning简介
    IGD反转世代距离-多目标优化评价指标概念及实现
    matlab sum函数
    多目标优化拥挤距离计算
    [转]matlab 中的波浪号
    多目标优化按支配关系分层实现
    Matlab矩阵加入新元素
  • 原文地址:https://www.cnblogs.com/yuanweidao/p/10805461.html
Copyright © 2011-2022 走看看