KM 算法

可能需要先去学学匈牙利算法等二分图相关知识。


模板题-洛谷P6577 【模板】二分图最大权完美匹配

给 \(n\) 和 \(m\) 与边 \(u_i,v_i,w_i(1\le i\le m)\)。有一个二分图,两边各 \(n\) 个点,共 \(m\) 条边,保证有完美匹配,求完美匹配最大边权之和。

数据范围:\(1\le n\le 500\),\(1\le m\le \frac{n\times (n-1)}{2}\),\(-19980731\le w_i \le 19980731\),无重边。


卡网络流以及一切复杂度 \(> \Theta(n^3)\) 的算法,卡不掉怪良心出题人。


  • 奇奇怪怪的定义

顶标:两边点都有的标记(左 \(a_i\) 右 \(b_j\))满足 \(a_i+b_j\ge w_{i,j}\),不唯一。

相等边:\(a_i+b_j=w_{i,j}\) 的边 \((i,j)\)。

相等子图:相等边构成的子图。

交错树:增广路径形成的树。

\(\tt KM\) 算法的结论:\(\color{#f00}{\texttt{当每个相等子图完备匹配时,二分图得到最大匹配。}}\)

因为显然,因为这个时候不可能有比它更优的匹配。


  • 奇奇怪怪的算法

很明显,并不是所有 的顶标分配方案都能使“每个相等子图完备匹配”的。

但是,找到一个可行的 顶标分配方案是很简单的,所以可以找到一种顶标分配然后找增广路的同时调整。

然后在发现相等子图的完备匹配后就匹配。

具体流程:

\((1)\) 分配可行顶标,并对每个节点执行 \((2),(3),(4)\)。

\((2)\) 匈牙利算法找增广。

\((3)\) 找不到增广路(相等子图匹配)就调整顶标。

\((4)\) 重复 \((2),(3)\) 直到找到增广路。


  • 代码

分析一下代码可知实际时间复杂度 \(\Theta(n^4)\)。

  1. //Data
  2. const ll N=500;
  3. ll n,m,e[N+7][N+7];
  4. //KM
  5. ll mat[N+7],d[N+7],va[N+7],vb[N+7],ak[N+7],bk[N+7];
  6. ll Dfs(ll u){
  7. va[u]=1;
  8. for(ll v=1;v<=n;v++)if(!vb[v]){
  9. if(ak[u]+bk[v]-e[u][v]==0){
  10. vb[v]=1;
  11. if(!mat[v]||Dfs(mat[v])) return mat[v]=u,1;
  12. } else d[v]=min(d[v],ak[u]+bk[v]-e[u][v]);
  13. }
  14. return 0;
  15. }
  16. ll KM(){
  17. fill(ak+1,ak+n+1,-INF);
  18. for(ll u=1;u<=n;u++)
  19. for(ll v=1;v<=n;v++) ak[u]=max(ak[u],e[u][v]);
  20. for(ll u=1;u<=n;u++){
  21. while(true){
  22. fill(va+1,va+n+1,0);
  23. fill(vb+1,vb+n+1,0);
  24. fill(d+1,d+n+1,INF);
  25. if(Dfs(u)) break;
  26. ll c=INF;
  27. for(ll v=1;v<=n;v++)if(!vb[v]) c=min(c,d[v]);
  28. for(ll v=1;v<=n;v++)if(va[v]) ak[v]-=c;
  29. for(ll v=1;v<=n;v++)if(vb[v]) bk[v]+=c;
  30. }
  31. }
  32. ll res=0;
  33. for(ll v=1;v<=n;v++) res+=e[mat[v]][v];
  34. return res;
  35. }
  36. //Main
  37. int main(){
  38. scanf("%lld%lld",&n,&m);
  39. for(ll u=1;u<=n;u++)
  40. for(ll v=1;v<=n;v++) e[u][v]=-INF;
  41. for(ll i=1;i<=m;i++){
  42. ll u,v,w;
  43. scanf("%lld%lld%lld",&u,&v,&w);
  44. e[u][v]=max(e[u][v],w);
  45. }
  46. printf("%lld\n",KM());
  47. for(ll u=1;u<=n;u++) printf("%lld ",mat[u]);puts("");
  48. return 0;
  49. }

这时候可以得 \(50\) 分,剩余的 \(\tt TLE\)。

废话:不得不佩服出题人!大部分人的 \(\tt KM\) 算法都是上面这么写的,要知道还有 \(\Theta(n^3)\) 的 \(\tt KM\),得找遍全网吧!我找了一个下午终于找到了,希望写了这篇文章后,大家就不需要像我这么累了!


  • 奇奇怪怪的优化

就是把 \(\tt Dfs\) 换成 \(\tt Bfs\)。本质和上面代码是一样的。

每个左边的点只会进队、搜索一次。\(\tt p\) 数组记录的是增广交错树。

这个 \(\tt Bfs\) 是迭代写的,所以不需要 \(\tt queue\)。


  • 代码

随机数据下是 \(\Theta(n^3)\),听说可以卡成 \(\Theta(n^4)\)。但是这样卡貌似没意义。

  1. //Data
  2. const int N=500;
  3. int n,m,e[N+7][N+7];
  4. //KM
  5. int mb[N+7],vb[N+7],ka[N+7],kb[N+7],p[N+7],c[N+7];
  6. int qf,qb,q[N+7];
  7. void Bfs(int u){
  8. int a,v=0,vl=0,d;
  9. for(int i=1;i<=n;i++) p[i]=0,c[i]=inf;
  10. mb[v]=u;
  11. do {
  12. a=mb[v],d=inf,vb[v]=1;
  13. for(int b=1;b<=n;b++)if(!vb[b]){
  14. if(c[b]>ka[a]+kb[b]-e[a][b])
  15. c[b]=ka[a]+kb[b]-e[a][b],p[b]=v;
  16. if(c[b]<d) d=c[b],vl=b;
  17. }
  18. for(int b=0;b<=n;b++)
  19. if(vb[b]) ka[mb[b]]-=d,kb[b]+=d;
  20. else c[b]-=d;
  21. v=vl;
  22. } while(mb[v]);
  23. while(v) mb[v]=mb[p[v]],v=p[v];
  24. }
  25. ll KM(){
  26. for(int i=1;i<=n;i++) mb[i]=ka[i]=kb[i]=0;
  27. for(int a=1;a<=n;a++){
  28. for(int b=1;b<=n;b++) vb[b]=0;
  29. Bfs(a);
  30. }
  31. ll res=0;
  32. for(int b=1;b<=n;b++) res+=e[mb[b]][b];
  33. return res;
  34. }
  35. //Main
  36. int main(){
  37. n=ri,m=ri;
  38. for(int a=1;a<=n;a++)
  39. for(int b=1;b<=n;b++) e[a][b]=-inf;
  40. for(int i=1;i<=m;i++){
  41. int u=ri,v=ri,w=ri;
  42. e[u][v]=max(e[u][v],w);
  43. }
  44. printf("%lld\n",KM());
  45. for(int u=1;u<=n;u++) printf("%d ",mb[u]);puts("");
  46. return 0;
  47. }

是不是看起来特别玄学?\(\tt KM\) 这种偏僻又难懂的算法,或许还是背板子好。

对了,然后就能 \(\tt AC\) 了。


祝大家学习愉快!

KM 算法的更多相关文章

  1. 匈牙利算法与KM算法

    匈牙利算法 var i,j,k,l,n,m,v,mm,ans:longint; a:..,..]of longint; p,f:..]of longint; function xyl(x,y:long ...

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

    Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description ...

  3. HDU2255-奔小康赚大钱-二分图最大权值匹配-KM算法

    二分图最大权值匹配问题.用KM算法. 最小权值的时候把权值设置成相反数 /*-------------------------------------------------------------- ...

  4. KM算法及其优化的学习笔记&&bzoj2539: [Ctsc2000]丘比特的烦恼

    感谢  http://www.cnblogs.com/vongang/archive/2012/04/28/2475731.html 这篇blog里提供了3个链接……基本上很明白地把KM算法是啥讲清楚 ...

  5. poj 2195 KM算法

    题目链接:http://poj.org/problem?id=2195 KM算法模板~ 代码如下: #include "stdio.h" #include "string ...

  6. hdu 2255 奔小康赚大钱--KM算法模板

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2255 题意:有N个人跟N个房子,每个人跟房子都有一定的距离,现在要让这N个人全部回到N个房子里面去,要 ...

  7. HDU(2255),KM算法,最大权匹配

    题目链接 奔小康赚大钱 Time Limit: 1000/1000MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Su ...

  8. 二分图 最大权匹配 km算法

    这个算法的本质还是不断的找增广路: KM算法的正确性基于以下定理:若由二分图中所有满足A[i]+B[j]=w[i,j]的边(i,j)构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最 ...

  9. hdu 2255 奔小康赚大钱 KM算法

    看到这么奇葩的题目名我笑了,后来这么一个裸的KM调了2小时我哭了…… 这是个裸的KM算法,也没什么多说的,主要是注意多组数据时,每次都要把各种数组清空啊,赋值啊什么的,反正比较麻烦.至于为什么调了2小 ...

  10. hdu 2853 Assignment KM算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2853 Last year a terrible earthquake attacked Sichuan ...

随机推荐

  1. 谷歌Colab使用(深度学习)

    1. Coalb简介 Google Colaboratory是谷歌开放的一款研究工具,主要用于机器学习的开发和研究.这款工具现在可以免费使用,但是不是永久免费暂时还不确定.Google Colab最大 ...

  2. JsonPath在接口自动化中的应用

    我理解jsonpath于json而言,就像是xpath在XML中的作用.用来确定json中某部分数据的语言.我更喜欢叫jsonpath表达式,因为这样好像是数学问题. 以前和小伙伴一起写接口自动化的时 ...

  3. NO.A.0009——day04——idea的安装及配置教程

    概述: 集成开发环境:IDE.开发工具Integrated Development Environment,IDE, 1.如果自己手洗衣服: 1. 准备一盆水 2. 放入衣服浸泡30分钟 3. 搓洗衣 ...

  4. linux服务器远程网络开机(wake on lan)

    通过网络可以远程开关机,某些时候比较方便管理机器 检查服务器是否支持远程网络开机 [root@lab5101 ~]# ethtool eth0 Settings for eth0: Supported ...

  5. wget 快速下载 ftp 文件

    GNU Wget 1.17.1,非交互式的网络文件下载工具. 用法: wget [选项]... [URL]... 长选项所必须的参数在使用短选项时也是必须的. 启动: -V, --version 显示 ...

  6. 建议收藏!2020阿里面试题(JVM+Spring Cloud+微服务)上

    前言 对于大厂面试,我想要强调的一点就是心态真的很重要,是决定你在面试过程中发挥的关键,若不能正常发挥,很可能就因为一个小失误与offer失之交臂,所以一定要重视起来.另外提醒一点,充分复习,是消除你 ...

  7. mongodb分页查询,排序

    mongodb代码 根据时间倒序,查看10条 db.表名.find({"_id":"xxx"}).sort({"inserted":-1}) ...

  8. 基于HAL库的STM32的DSP库详解(附FFT应用)

    1 . 建立工程,生成代码时选择包含所有库.   2. 打开 option for target 选择 Target 标签,在code generatio中,将floating point hardw ...

  9. 交换机Access、Trunk和Hybrid 接口类型及区别

    交换机接口的类型可以是 Access.Trunk和Hybrid. Access类型的接口仅属于一个VLAN,只能接收.转发相应VLAN的帧: Trunk类型接口则默认属于所有VLAN,任何 Tagge ...

  10. java43

    自定义日期格式 import java.text.DateFormat; import java.text.ParseException; import java.util.Date; public ...