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. 同步FIFO学习笔记

  2. Scrum转型(一) 为什么敏捷和Scrum

    1.1 为什么敏捷 由于传统的瀑布模型管理方法无法满足现代某些软件产品开发过程的特点,我们需要使用敏捷的方法(例如,Scrum是一个让我们关注于在短时间里交付高质量商业价值的敏捷框架). 需求频繁变动 ...

  3. 《JavaScript高级程序设计》读书笔记 ---继承

    继承是OO 语言中的一个最为人津津乐道的概念.许多OO 语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法.如前所述,由于函数没有签名,在ECMAScrip ...

  4. 这行代码告诉你!为什么你地下城与勇士(DNF)的装备强化老是失败?

    模拟地下城与勇士(DNF)的装备强化 tip1: DNF装备强化在+1-+3 不会失败: +4-+7,失败后物品原有强化等级降低1级: +8-+10,失败后掉3级: 10上11或以上就爆了. tip2 ...

  5. 深度分析:Java 静态方法/变量,非静态方法/变量的区别,今天一并帮你解决!

    静态/非静态 方法/变量的写法 大家应该都明白静态方法/字段比普通方法/字段的写法要多一个static关键字,简单写下他们的写法吧,了解的可以直接略过 class Test{ // 静态变量 publ ...

  6. 怎么用iMindMap思维导图做自我介绍

    相信大家在工作生活当中对于自我介绍这件事情都很是头疼的.太简单的介绍重点不突出,太冗长的介绍又会让人没印象.这一切都跟我们大脑水平.散乱的思维模式有关,因此想要自我介绍在众人中脱颖而出,让人印象深刻, ...

  7. 如何基于App SDK快速地开发一个IoT App?

    一.背景及大纲介绍 在如今物联网DCM(Device.Connect.Manage)的大框架下,有一个应用层来分析和处理数据,是必备技能.但是,对于一个公司来说,因为研发能力或者研发时间的原因,可能很 ...

  8. 基于gin的golang web开发:docker

    Golang天生适合运行在docker容器中,这得益于:Golang的静态编译,当在编译的时候关闭cgo的时候,可以完全不依赖系统环境. 一些基础 测试容器时我们经常需要进入容器查看运行情况,以下命令 ...

  9. J - Sushi 题解(期望dp)

    题目链接 题目大意 给你n个盘子,每个盘子可能有1,2,3个披萨 你选到每个盘子的概率是一样的. 你如果选到空的盘子什么都不做 如果你选到有披萨的盘子则吃掉一个披萨 求吃完所有披萨的期望 题目思路 设 ...

  10. C语言讲义——链表的实现

    节点(结构体描述) struct Node { int _id; char s[50]; struct Node* pre;// 指向前一个节点的地址 struct Node* next;// 指向下 ...