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)\)。

//Data
const ll N=500;
ll n,m,e[N+7][N+7]; //KM
ll mat[N+7],d[N+7],va[N+7],vb[N+7],ak[N+7],bk[N+7];
ll Dfs(ll u){
va[u]=1;
for(ll v=1;v<=n;v++)if(!vb[v]){
if(ak[u]+bk[v]-e[u][v]==0){
vb[v]=1;
if(!mat[v]||Dfs(mat[v])) return mat[v]=u,1;
} else d[v]=min(d[v],ak[u]+bk[v]-e[u][v]);
}
return 0;
}
ll KM(){
fill(ak+1,ak+n+1,-INF);
for(ll u=1;u<=n;u++)
for(ll v=1;v<=n;v++) ak[u]=max(ak[u],e[u][v]);
for(ll u=1;u<=n;u++){
while(true){
fill(va+1,va+n+1,0);
fill(vb+1,vb+n+1,0);
fill(d+1,d+n+1,INF);
if(Dfs(u)) break;
ll c=INF;
for(ll v=1;v<=n;v++)if(!vb[v]) c=min(c,d[v]);
for(ll v=1;v<=n;v++)if(va[v]) ak[v]-=c;
for(ll v=1;v<=n;v++)if(vb[v]) bk[v]+=c;
}
}
ll res=0;
for(ll v=1;v<=n;v++) res+=e[mat[v]][v];
return res;
} //Main
int main(){
scanf("%lld%lld",&n,&m);
for(ll u=1;u<=n;u++)
for(ll v=1;v<=n;v++) e[u][v]=-INF;
for(ll i=1;i<=m;i++){
ll u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
e[u][v]=max(e[u][v],w);
}
printf("%lld\n",KM());
for(ll u=1;u<=n;u++) printf("%lld ",mat[u]);puts("");
return 0;
}

这时候可以得 \(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)\)。但是这样卡貌似没意义。

//Data
const int N=500;
int n,m,e[N+7][N+7]; //KM
int mb[N+7],vb[N+7],ka[N+7],kb[N+7],p[N+7],c[N+7];
int qf,qb,q[N+7];
void Bfs(int u){
int a,v=0,vl=0,d;
for(int i=1;i<=n;i++) p[i]=0,c[i]=inf;
mb[v]=u;
do {
a=mb[v],d=inf,vb[v]=1;
for(int b=1;b<=n;b++)if(!vb[b]){
if(c[b]>ka[a]+kb[b]-e[a][b])
c[b]=ka[a]+kb[b]-e[a][b],p[b]=v;
if(c[b]<d) d=c[b],vl=b;
}
for(int b=0;b<=n;b++)
if(vb[b]) ka[mb[b]]-=d,kb[b]+=d;
else c[b]-=d;
v=vl;
} while(mb[v]);
while(v) mb[v]=mb[p[v]],v=p[v];
}
ll KM(){
for(int i=1;i<=n;i++) mb[i]=ka[i]=kb[i]=0;
for(int a=1;a<=n;a++){
for(int b=1;b<=n;b++) vb[b]=0;
Bfs(a);
}
ll res=0;
for(int b=1;b<=n;b++) res+=e[mb[b]][b];
return res;
} //Main
int main(){
n=ri,m=ri;
for(int a=1;a<=n;a++)
for(int b=1;b<=n;b++) e[a][b]=-inf;
for(int i=1;i<=m;i++){
int u=ri,v=ri,w=ri;
e[u][v]=max(e[u][v],w);
}
printf("%lld\n",KM());
for(int u=1;u<=n;u++) printf("%d ",mb[u]);puts("");
return 0;
}

是不是看起来特别玄学?\(\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. ELF文件格式内容

    在计算机科学中,是一种用于二进制文件.可执行文件.目标代码.共享库和核心转储格式文件.   ELF文件组成部分 ELF文件由4部分组成,分别是ELF头(ELF header).程序头表(Program ...

  2. 利用HUtool读取Excel内容

    // 1.获取上传文件输入流 InputStream inputStream = null; try{ inputStream = file.getInputStream(); }catch (Exc ...

  3. PID算法的C语言实现

    1.根据我控制算法类文章中关于PID的理论的一些描述,同时也根据网络上一些其他的PID文章,以及自己最近一个项目的实践后,总结了几套基于C语言的PID算法,由于网络中很少有人进行分享完整的PID算法实 ...

  4. matlab 数组操作作业

    写出下列语句的计算结果及作用 1.A= [2 5 7 3 1 3 4 2];    创建二维数组并赋值 2.[rows, cols] = size(A);    ​把A的尺寸赋值给数组,rows为行, ...

  5. Linux学习 - 02 使用 - Centos8 - 网络配置相关

    『Centos8 网络配置』 题外话:最近太忙,利用仅有的周末空闲时间记录点东西,草率了. 问题1:安装 Centos8.2 minimal 过程中,只是设置了 WiFi的静态IP,没有进行[以太网] ...

  6. NLP之统计句法分析(PCFG+CYK算法)

    一.认识句法分析 首先,了解一下句法分析到底是什么意思?是做什么事情呢?顾名思义,感觉是学习英语时候讲的各种句法语法.没错!这里就是把句法分析过程交给计算机处理,让它分析一个句子的句法组成,然后更好理 ...

  7. Poem Codes - 攻防世界(Decrypt-the-Message)

    Poem Codes Poem Code 最显著的特点就是一首诗歌. 详情请戳这里 让我们一起来过滤一遍这个神奇的加密过程~ ① 给出一首诗歌 for my purpose holds to sail ...

  8. 看看吧!月薪20K以上的程序员才能全部掌握RabbitMq知识,你掌握了多少

    一.RabbitMq基础知识 0.概述 消息队列的作用就是接收消息生产者的消息,然后将消息发送到消费者 1.信道channel 我的理解是生产者/消费者和rabbitmq交互的一个通道,负责交换机.队 ...

  9. How to realize one's ambition

    Work Overtime Can it work? To some extent, it parhaps works very well. What if you do little job and ...

  10. FL Studio乐理教程之调式音阶

    在我们使用FL制作音乐时,乐理是必不可少的制作基础,本篇教程将结合FL Studio为大家讲解基础乐理及在FL Studio20中的使用技巧. 添加一个乐器,打开Piano Roll(钢琴窗). 首先 ...