基本概念

二分图有两个种点:X和Y。X与Y之间存在一些边,每个边有一个权值。现要求求一组X与Y间的通过边实现的一一匹配,使得得到的边权和最大。

总体过程

对每个X节点设置一个顶标Xl,初值为与X相邻的边的权值最大值;Y节点设置一个顶标Yl,初值为0。当前情况下,如果Xl[x]+Yl[x]==weight[x][y],则此时的边(x,y)为可匹配边。weight[x][y]越接近Xl[x]+Yl[y],则边(x,y)越有可能为匹配边。

枚举每一个X,对以下步骤循环:对于当前图所有可匹配的边所形成的子图用匈牙利算法进行交错路径搜索。搜索到了交错路径那皆大欢喜,把交错路径上的匹配边翻转一下,重新设置与y节点匹配的x,便完成了对当前X的匹配;否则,更改原有的匹配方式,使得边权和变小的程度尽量小,且匹配的边数不变,再接着找交错路径……。此过程一直持续到寻找到交错路径为止。

如何更改原有的匹配方式?

利用匈牙利算法中访问的节点标记,在访问到的X节点和未访问到的Y节点中找到一对x和y,使得delta=xl[x]+yl[y]-weight[x][y]最小,然后将Vis标记过的Xl[x]-=delta, Yl[y]+=delta,此时Xl[x]+Yl[y]==weight[x][y],相当于将xy匹配,并将原先与y相连的x'节点断开。其他边不变。这样匹配边的总数不变。

对于delta,我们可以再在FindPath中把所有x节点的delta最小值预先算出来。

注意事项

  • Xl[x]+Yl[y]>=XYweight[x][y]。
  • 最后统计边权和时,按照YmatchX搜索,而不是通过可匹配边(Xl[x]+Yl[y]==weight[x][y])搜索。
  • X也有Vis,求交错路径时不要忘了设置X的Vis。
  • FindPath算Delta时,else后的内容应当放在if(Xl[x]+Yl[y]==XYweight[x][y])下,而不是if(!Yvis[y])因为delta的定义是vis过的x与没有vis过的y的最小delta值
  • 设置Yvis=true应当放在if(Xl[x]+Yl[y]==XYweight[x][y])内,而不是其外部、if(!Yvis[y])内。因为只有Xl[x]+Yl[y]==XYweight[x][y]时边(x,y)才是匹配边。
    1. #include <cstdio>
    2. #include <cstring>
    3. #include <algorithm>
    4. using namespace std;
    5.  
    6. const int MAX_X = 1010, MAX_Y = 1010, INF = 0x3f3f3f3f;
    7. #define LOOP(i, n) for(int i=1; i<=n; i++)
    8.  
    9. struct KM
    10. {
    11. int XYweight[MAX_X][MAX_Y];
    12. int YmatchX[MAX_Y];
    13. int Xl[MAX_X], Yl[MAX_Y];
    14. int Delta[MAX_X];
    15. bool Yvis[MAX_Y], Xvis[MAX_X];
    16. int totX, totY;
    17.  
    18. bool FindPath(int x)
    19. {
    20. Xvis[x] = true;
    21. LOOP(y, totY)
    22. {
    23. if (!Yvis[y])
    24. {
    25. if (Xl[x] + Yl[y] == XYweight[x][y])
    26. {
    27. Yvis[y] = true;
    28. if (!YmatchX[y] || FindPath(YmatchX[y]))
    29. {
    30. YmatchX[y] = x;
    31. return true;
    32. }
    33. }
    34. else
    35. Delta[x] = min(Delta[x], Xl[x] + Yl[y] - XYweight[x][y]);
    36. }
    37. }
    38. return false;
    39. }
    40.  
    41. int Proceed()
    42. {
    43. memset(YmatchX, 0, sizeof(YmatchX));
    44. memset(Xl, 0, sizeof(Xl));
    45. memset(Yl, 0, sizeof(Yl));
    46. LOOP(x, totX)
    47. LOOP(y, totY)
    48. Xl[x] = max(Xl[x], XYweight[x][y]);
    49. LOOP(firstX, totX)
    50. {
    51. while (true)
    52. {
    53. memset(Xvis, false, sizeof(Xvis));
    54. memset(Yvis, false, sizeof(Yvis));
    55. memset(Delta, INF, sizeof(Delta));
    56. if (FindPath(firstX))
    57. break;
    58. int delta = INF;
    59. LOOP(x, totX)
    60. if (Xvis[x])
    61. delta = min(delta, Delta[x]);
    62. if (delta == INF)
    63. break;
    64. LOOP(x, totX)
    65. if (Xvis[x])
    66. Xl[x] -= delta;
    67. LOOP(y, totY)
    68. if (Yvis[y])
    69. Yl[y] += delta;
    70. }
    71. }
    72. int ans = 0;
    73. LOOP(y, totY)
    74. if (YmatchX[y])
    75. ans += XYweight[YmatchX[y]][y];
    76. return ans;
    77. }
    78. }g;
    79.  
    80. int main()
    81. {
    82. int tot;
    83. while (~scanf("%d", &tot))
    84. {
    85. g.totX = tot;
    86. g.totY = tot;
    87. LOOP(x, tot)
    88. {
    89. LOOP(y, tot)
    90. {
    91. int w;
    92. scanf("%d", &w);
    93. g.XYweight[x][y] = w;
    94. }
    95. }
    96. printf("%d\n", g.Proceed());
    97. }
    98. return 0;
    99. }

      

  

HDU2255 奔小康赚大钱 【模板】 二分图完美匹配的更多相关文章

  1. HDU2255 奔小康赚大钱 (最大权完美匹配) 模板题【KM算法】

    <题目链接> 奔小康赚大钱 Problem Description 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子.这可是一件大事,关系到人民的住房问题啊 ...

  2. HDU2255 奔小康赚大钱【二分图最佳匹配】

    题目链接: http://acm.hdu.edu.cn/showproblem.php? pid=2255 题目大意: 村里要分房子. 有N家老百姓,刚好有N间房子.考虑到每家都要有房住,每家必须分配 ...

  3. hdu-2255.奔小康赚大钱(最大权二分匹配)

    奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Subm ...

  4. Hdu2255 奔小康赚大钱(二分图最大权匹配KM算法)

    奔小康赚大钱 Problem Description 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子. 这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好 ...

  5. hdu2255 奔小康赚大钱,最大权匹配,KM算法

    点击打开链接 最大权匹配 KM算法 算法步骤: 设顶点Xi的顶标为a[i],顶点Yi的顶标为b[i] ⅰ.初始时.a[i]为与Xi相关联的边的最大权值.b[j]=0.保证a[i]+b[j]>=w ...

  6. hdu2255 奔小康赚大钱 km算法解决最优匹配(最大权完美匹配)

    /** 题目:hdu2255 奔小康赚大钱 km算法 链接:http://acm.hdu.edu.cn/showproblem.php?pid=2255 题意:lv 思路:最优匹配(最大权完美匹配) ...

  7. HDU2255 奔小康赚大钱 —— 二分图最大权匹配 KM算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2255 奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others)    ...

  8. hdu2255 奔小康赚大钱 二分图最佳匹配--KM算法

    传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子.这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住(如果有老百姓没房子住 ...

  9. HDU2255 奔小康赚大钱 【KM算法】

    题意: 每个人对不同房有不同出价,就是就是怎样匹配卖房让收入达到最大. 思路: 建立二分图,一边为N家老百姓,还有一边为N间房子.对老百姓和房子之间估价建立一条有带权边.问题就转变为了再二分图中找出一 ...

  10. [hdu2255] 奔小康赚大钱

    Description 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子. 这可是一件大事,关系到人民的住房问题啊.村里共有 \(n\) 间房间,刚好有 \(n\) 家 ...

随机推荐

  1. SPOJ GSS4 Can you answer these queries IV ——树状数组 并查集

    [题目分析] 区间开方+区间求和. 由于区间开方次数较少,直接并查集维护下一个不是1的数的位置,然后暴力修改,树状数组求和即可. 这不是BZOJ上上帝造题7分钟嘛 [代码] #include < ...

  2. [BZOJ4994] [Usaco2017 Feb]Why Did the Cow Cross the Road III(树状数组)

    传送门 1.每个数的左右位置预处理出来,按照左端点排序,因为左端点是从小到大的,我们只需要知道每条线段包含了多少个前面线段的右端点即可,可以用树状数组 2.如果 ai < bj < bi, ...

  3. POJ 2155 Matrix【二维线段树】

    题目大意:给你一个全是0的N*N矩阵,每次有两种操作:1将矩阵中一个子矩阵置反,2.查询某个点是0还是1 思路:裸的二维线段树 #include<iostream>#include< ...

  4. 算法复习——cdq分治

    题目: Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要 ...

  5. 算法复习——floyd求最小环(poj1734)

    题目: 题目描述 N 个景区,任意两个景区之间有一条或多条双向的路来连接,现在 Mr.Zeng 想找一条旅游路线,这个路线从A点出发并且最后回到 A 点,假设经过的路线为 V1,V2,....VK,V ...

  6. 常州模拟赛d4t2 陶陶摘苹果

    题目描述 陶陶家的院子里有一棵苹果树,每到秋天树上就会结出 n 个苹果.苹果成熟的时候,陶陶就会 跑去摘苹果. 陶陶的手不能弯 (他仅能把手伸直),当且仅当陶陶达到的高度与苹果的高度相等的时候,陶陶 ...

  7. spring和mybatis整合配置文件

    查看所有springmvc  spring  mybatis配置文件见下链接: https://my.oschina.net/sherwayne/blog/262616 <?xml versio ...

  8. linux的cpu性能评估

    linux的cpu性能评估 参考自:自学it网,http://www.zixue.it/. (1)利用vmstat命令监控系统CPU[test@localhost ~]$ vmstat 2 3 #每2 ...

  9. php——配合QQ邮箱发送邮件

    最近做一个域名管理系统的项目,实现在域名还有三十天的时候系统发送邮件到QQ邮箱从而提醒续费: 这里运用到了phpmailer; 1:getAll函数: function getAll($con,$sq ...

  10. (1)Swing创建窗体

    本系列使用Intellij IDEA 2017.3.4版本 一.运行窗体 1. 2. 3. 4. 5. 6. 给JPanel起个名字 -如From 7. 8. 9. 生成 import javax.s ...