KM bfs写法

2018astar资格赛的第三题整数规划

把\(x, y\)看成二分图两边的顶标,\(a_{ij}\)就是二分图的边权,整道题其实就是求二分图的最大权匹配。

然后打了个\(dfs\)的\(KM\),\(TLE\)了,后来听别人说要用\(bfs\)的写法,因为那个才是真正的\(O(n^3)\),\(dfs\)的写法最坏情况还是\(O(n^4)\)。

原理是一样的,只不过\(bfs\)有一点点像迭代,每一次也只是搜\(diff=0\)的情况,而且右边的点只会搜索一次(或者说是左边的点只会搜索一次,即左边的每个点只会进队一次),用\(pre\)记住当前的交错路径,找到未匹配的就可以沿交错路径进行修改。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const int maxn=210;
  5. const LL inf=1LL<<60;
  6. int n;
  7. namespace KM
  8. {
  9. int n;
  10. LL mat[maxn][maxn]; //边权
  11. int matcha[maxn], matchb[maxn]; //左边的点匹配的右边点;右边的点匹配的左边点
  12. LL marka[maxn], markb[maxn]; //左顶标;右顶标
  13. LL slack[maxn]; //松弛数组
  14. bool visa[maxn], visb[maxn]; //访问标记
  15. int head, tail;
  16. int q[maxn], pre[maxn]; //队列;交错路径
  17. bool check(int cur)
  18. {
  19. visb[cur]=true; //标记cur已搜索
  20. if (matchb[cur]) //已匹配,即当前匹配失败
  21. {
  22. if (!visa[matchb[cur]]) //匹配的点是否已进队
  23. {
  24. q[++tail]=matchb[cur];
  25. visa[matchb[cur]]=true;
  26. }
  27. return false;
  28. }
  29. //未匹配,即当前匹配成功,沿交错路径进行匹配
  30. while (cur)
  31. swap(cur, matcha[matchb[cur]=pre[cur]]);
  32. return true;
  33. }
  34. void bfs(int start)
  35. {
  36. fill(visa, visa+1+n, false);
  37. fill(visb, visb+1+n, false);
  38. fill(slack, slack+1+n, inf);
  39. head=tail=1;
  40. q[1]=start;
  41. visa[start]=true;
  42. while (1)
  43. {
  44. while (head<=tail)
  45. {
  46. int cur=q[head++];
  47. for (int i=1; i<=n; ++i)
  48. {
  49. LL diff=marka[cur]+markb[i]-mat[cur][i];
  50. if (!visb[i] && diff<=slack[i]) //visb=true说明已搜索,无需更新slack和pre,也是保证pre的正确性
  51. {
  52. slack[i]=diff;
  53. pre[i]=cur;
  54. if (diff==0) //diff=0,可以尝试匹配
  55. if (check(i)) return; //匹配成功可直接返回
  56. }
  57. }
  58. }
  59. LL delta=inf;
  60. for (int i=1; i<=n; ++i)
  61. if (!visb[i] && slack[i]) delta=min(slack[i], delta);
  62. for (int i=1; i<=n; ++i) //松弛
  63. {
  64. if (visa[i]) marka[i]-=delta;
  65. if (visb[i]) markb[i]+=delta;
  66. else slack[i]-=delta; //维护slack的正确性(参考diff的计算及marka,markb的变化)
  67. }
  68. head=1, tail=0;
  69. for (int i=1; i<=n; ++i)
  70. if (!visb[i] && !slack[i] && check(i)) return;
  71. //松弛后尝试匹配diff=0的点。
  72. }
  73. }
  74. void solve()
  75. {
  76. fill(matcha, matcha+1+n, 0);
  77. fill(matchb, matchb+1+n, 0);
  78. fill(markb, markb+1+n, 0);
  79. for (int i=1; i<=n; ++i)
  80. {
  81. marka[i]=0;
  82. for (int j=1; j<=n; ++j)
  83. marka[i]=max(marka[i], mat[i][j]);
  84. }
  85. for (int i=1; i<=n; ++i) bfs(i);
  86. }
  87. }
  88. void read()
  89. {
  90. scanf("%d", &n);
  91. KM::n=n;
  92. for (int i=1; i<=n; ++i)
  93. for (int j=1; j<=n; ++j)
  94. {
  95. int x;
  96. scanf("%d", &x);
  97. KM::mat[i][j]=-x;
  98. }
  99. }
  100. void solve()
  101. {
  102. KM::solve();
  103. LL ans=0;
  104. for (int i=1; i<=n; ++i)
  105. ans+=KM::marka[i]+KM::markb[i];
  106. printf("%lld\n", -ans);
  107. }
  108. int main()
  109. {
  110. int casesum;
  111. scanf("%d", &casesum);
  112. for (int i=1; i<=casesum; ++i)
  113. {
  114. printf("Case #%d: ", i);
  115. read();
  116. solve();
  117. }
  118. return 0;
  119. }

KM bfs写法的更多相关文章

  1. POJ1915Knight Moves(单向BFS + 双向BFS)

    题目链接 单向bfs就是水题 #include <iostream> #include <cstring> #include <cstdio> #include & ...

  2. Addition Chains POJ - 2248 (bfs / dfs / 迭代加深)

    An addition chain for n is an integer sequence <a0, a1,a2,...,am=""> with the follow ...

  3. uoj#80 二分图最大权匹配

    题意:给定二分图,有边权,求最大边权匹配.边权非负. 解:KM算法求解最大权完备匹配. 完备匹配就是点数少的那一边每个点都有匹配. 为了让完备匹配与最大权匹配等价,我们添加若干条0边使之成为完全二分图 ...

  4. 【bzoj1060】[ZJOI2007]时态同步

    题目描述 小Q在电子工艺实习课上学习焊接电路板.一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数字1,2,3-.进行标号.电路板的各个节点由若干不相交的导线相连接,且对于电路板的任何两个节点 ...

  5. APIO2017商旅

    传送门(PDF) 题目大意:有$N$个点,$M$条有向边,$K$种物品,在不同的点可以用不同的价格买入或卖出某一种商品. 任意时刻至多持有一种物品,不能在同一个点先买再卖,求收益与长度之比最大的点数$ ...

  6. [LeetCode] 864. Shortest Path to Get All Keys 获得所有钥匙的最短路径

    We are given a 2-dimensional grid. "." is an empty cell, "#" is a wall, "@& ...

  7. 【剑指Offer】60、按之字形顺序打印二叉树

    题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推. 题解:BFS 主要的方法与BFS写法没什么区 ...

  8. bzoj3436小K的农场

    bzoj3436小K的农场 题意: n个数,知道m条关系:a-b≥c.a-b≤c或a==b.问是否存在满足所有关系的情况.n≤10000,m≤10000. 题解: 差分约束.因为只要求是否满足,因此最 ...

  9. 【LeetCode】代码模板,刷题必会

    目录 二分查找 排序的写法 BFS的写法 DFS的写法 回溯法 树 递归 迭代 前序遍历 中序遍历 后序遍历 构建完全二叉树 并查集 前缀树 图遍历 Dijkstra算法 Floyd-Warshall ...

随机推荐

  1. BZOJ 2186 沙拉公主的困惑(预处理逆元+欧拉函数)

    题意:求1-n!里与m!互质的数有多少?(m<=n<=1e6). 因为n!%m!=0,所以题目实际上求的是phi(m!)*n!/m!. 预处理出这些素数的逆元和阶乘的模即可. # incl ...

  2. BZOJ 1237 配对(DP)

    给出两个长度为n的序列.这两个序列的数字可以连边当且仅当它们不同,权值为它们的绝对值,求出这个二分图的最小权值完全匹配.没有输出-1. n<=1e5.用KM会TLE+MLE. 如果连边没有限制的 ...

  3. 插件-监控页面加载之loading

    查看效果点https://icedjuice.github.io/plug-in/loading/loading.html 简单易用的loading插件,该插件并不是真正的监控页面的资源加载过程,而是 ...

  4. DjangoORM外键操作

    Django ORM 外键操作 经常修改的东西一般不放到内存里面,而是放到一张表里.表跟表之间是可以存在关系的,最基本的就是一对多的关系. models.ForeignKey(ColorDic) 1. ...

  5. Android NDK 编译选项设置[zhuan]

    http://crash.163.com/#news/!newsId=24 在Android NDK开发中,有两个重要的文件:Android.mk和Application.mk,各尽其责,指导编译器如 ...

  6. 没有为扩展名“.cshtml”注册的生成提供程序。

    新建的mvc4 空项目,然后从其他项目里拷贝shared文件夹和_ViewStart.cshtml文件过去,然后在@符号上出现 没有为扩展名“.cshtml”注册的生成提供程序.错误 解决方法: 需要 ...

  7. struts2 文件下载的处理

  8. 题解【bzoj2038 [2009国家集训队]小Z的袜子(hose)】

    Description \(m\) 个询问,每次给出一个区间,求从这个区间中取出两个数使得它们同色的概率. \(n,m,a_i \leq 50000\) Solution 莫队模板题 最后的概率是 选 ...

  9. 题解【bzoj3240 [NOI2013]矩阵游戏】

    挖坑2333 等我把代码写完了再写

  10. Leetcode 492. 构造矩形

    1.题目描述 作为一位web开发者, 懂得怎样去规划一个页面的尺寸是很重要的. 现给定一个具体的矩形页面面积,你的任务是设计一个长度为 L 和宽度为 W 且满足以下要求的矩形的页面.要求: 1. 你设 ...