KM算法 详解+模板
先说KM算法求二分图的最佳匹配思想,再详讲KM的实现。
【KM算法求二分图的最佳匹配思想】
(以上为摘上网络)
【KM算法及其具体过程】
(1)可行点标:每个点有一个标号,记lx[i]为X方点i的标号,ly[j]为Y方点j的标号。如果对于图中的任意边(i, j, W)都有lx[i]+ly[j]>=W,则这一组点标是可行的。特别地,对于lx[i]+ly[j]=W的边(i, j, W),称为可行边;
(2)KM 算法的核心思想就是通过修改某些点的标号(但要满足点标始终是可行的),不断增加图中的可行边总数,直到图中存在仅由可行边组成的完全匹配为止,此时这个 匹配一定是最佳的(因为由可行点标的的定义,图中的任意一个完全匹配,其边权总和均不大于所有点的标号之和,而仅由可行边组成的完全匹配的边权总和等于所 有点的标号之和,故这个匹配是最佳的)。一开始,求出每个点的初始标号:lx[i]=max{e.W|e.x=i}(即每个X方点的初始标号为与这个X方 点相关联的权值最大的边的权值),ly[j]=0(即每个Y方点的初始标号为0)。这个初始点标显然是可行的,并且,与任意一个X方点关联的边中至少有一条可行边;
(3)然后,从每个X方点开始DFS增广。DFS增广的过程与最大匹配的Hungary算法基本相同,只是要注意两点:一是只找可行边,二是要把搜索过程中遍历到的X方点全部记下来(可以用vst搞一下),以进行后面的修改;
(4) 增广的结果有两种:若成功(找到了增广轨),则该点增广完成,进入下一个点的增广。若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的 数量增加。方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,所有在增广轨中的Y方点的标号全部加上一个常数d,则 对于图中的任意一条边(i, j, W)(i为X方点,j为Y方点):
<1>i和j都在增广轨中:此时边(i, j)的(lx[i]+ly[j])值不变,也就是这条边的可行性不变(原来是可行边则现在仍是,原来不是则现在仍不是);
<2>i在增广轨中而j不在:此时边(i, j)的(lx[i]+ly[j])的值减少了d,也就是原来这条边不是可行边(否则j就会被遍历到了),而现在可能是;
<3>j在增广轨中而i不在:此时边(i, j)的(lx[i]+ly[j])的值增加了d,也就是原来这条边不是可行边(若这条边是可行边,则在遍历到j时会紧接着执行DFS(i),此时i就会被遍历到),现在仍不是;
<4>i和j都不在增广轨中:此时边(i, j)的(lx[i]+ly[j])值不变,也就是这条边的可行性不变。
这 样,在进行了这一步修改操作后,图中原来的可行边仍可行,而原来不可行的边现在则可能变为可行边。那么d的值应取多少?显然,整个点标不能失去可行性,也 就是对于上述的第<2>类边,其lx[i]+ly[j]>=W这一性质不能被改变,故取所有第<2>类边的 (lx[i]+ly[j]-W)的最小值作为d值即可。这样一方面可以保证点标的可行性,另一方面,经过这一步后,图中至少会增加一条可行边。
(5)修改后,继续对这个X方点DFS增广,若还失败则继续修改,直到成功为止;
(6)以上就是KM算法的基本思路。但是朴素的实现方法,时间复杂度为O(n4)——需要找O(n)次增广路,每次增广最多需要修改O(n)次顶标,每次修改顶 标时由于要枚举边来求d值,复杂度为O(n2)。实际上KM算法的复杂度是可以做到O(n3)的。我们给每个Y顶点一个“松弛量”函数slack,每次开 始找增广路时初始化为无穷大。在寻找增广路的过程中,检查边(i,j)时,如果它不在相等子图中,则让slack[j]变成原值与 A[i]+B[j]-w[i,j]的较小值。这样,在修改顶标时,取所有不在交错树中的Y顶点的slack值中的最小值作为d值即可。但还要注意一点:修 改顶标后,要把所有不在交错树中的Y顶点的slack值都减去d。
【求二分图的最小匹配】
只需把权值取反,变为负的,再用KM算出最大权匹配,取反则为其最小权匹配。
hdoj 2255#include <stdio.h>
#include <string.h>
#define M 310
#define inf 0x3f3f3f3fint n,nx,ny;
int link[M],lx[M],ly[M],slack[M]; //lx,ly为顶标,nx,ny分别为x点集y点集的个数
int visx[M],visy[M],w[M][M];int DFS(int x)
{visx[x] = 1;for (int y = 1;y <= ny;y ++){if (visy[y])continue;int t = lx[x] + ly[y] - w[x][y];if (t == 0) //{visy[y] = 1;if (link[y] == -1||DFS(link[y])){link[y] = x;return 1;}}else if (slack[y] > t) //不在相等子图中slack 取最小的slack[y] = t;}return 0;
}
int KM()
{int i,j;memset (link,-1,sizeof(link));memset (ly,0,sizeof(ly));for (i = 1;i <= nx;i ++) //lx初始化为与它关联边中最大的for (j = 1,lx[i] = -inf;j <= ny;j ++)if (w[i][j] > lx[i])lx[i] = w[i][j];for (int x = 1;x <= nx;x ++){for (i = 1;i <= ny;i ++)slack[i] = inf;while (1){memset (visx,0,sizeof(visx));memset (visy,0,sizeof(visy));if (DFS(x)) //若成功(找到了增广轨),则该点增广完成,进入下一个点的增广break; //若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。//方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,//所有在增广轨中的Y方点的标号全部加上一个常数dint d = inf;for (i = 1;i <= ny;i ++)if (!visy[i]&&d > slack[i])d = slack[i];for (i = 1;i <= nx;i ++)if (visx[i])lx[i] -= d;for (i = 1;i <= ny;i ++) //修改顶标后,要把所有不在交错树中的Y顶点的slack值都减去dif (visy[i])ly[i] += d;elseslack[i] -= d;}}int res = 0;for (i = 1;i <= ny;i ++)if (link[i] > -1)res += w[link[i]][i];return res;
}
int main ()
{int i,j;while (scanf ("%d",&n)!=EOF){nx = ny = n;// memset (w,0,sizeof(w));for (i = 1;i <= n;i ++)for (j = 1;j <= n;j ++)scanf ("%d",&w[i][j]);int ans = KM();printf ("%d\n",ans);}return 0;
}
转载于:https://www.cnblogs.com/Chierush/p/3329712.html
KM算法 详解+模板相关推荐
- DFS (深度优先搜索) 算法详解 + 模板 + 例题,这一篇就够了
深度优先搜索算法(Depth First Search,简称DFS):一种用于遍历或搜索树或图的算法. 沿着树的深度遍历树的节点,尽可能深的搜索树的分支.当节点v的所在边都己被探寻过或者在搜寻时结点不 ...
- 奔小康赚大钱 HDU - 2255( 二分图匹配KM算法详解)
题目 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子. 这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住(如果有老百 ...
- 多目标跟踪算法中之图匹配——匈牙利算法和KM算法详解
目录 一.匈牙利算法 1.算法背景及思想 2.最大匹配 3.最优匹配/完美匹配 4.增广路径 5.代码实现 6.匈牙利算法总结 6.1.深度优先 6.2. 广度优先 二.KM算法思想及局限性 代码示例 ...
- Kruskal(克鲁斯卡尔) 最小生成树 算法详解+模板
最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树. 例如,对于如上图G4所示的连通网可以有多棵权值总和 ...
- 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离
首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...
- KMP算法详解P3375 【模板】KMP字符串匹配题解
KMP算法详解: KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt(雾)提出的. 对于字符串匹配问题(such as 问你在abababb中有多少个 ...
- 离线强化学习(Offline RL)系列3: (算法篇) Onestep 算法详解与实现
[更新记录] 论文信息: David Brandfonbrener, William F. Whitney, Rajesh Ranganath, Joan Bruna: "Offline R ...
- C++中的STL算法详解
1.STL算法详解 STL提供能在各种容器中通用的算法(大约有70种),如插入.删除.查找.排序等.算法就是函数模板,算法通过迭代器来操纵容器中的元素.许多算法操作的是容器上的一个区间(也可以是整个容 ...
- 基础排序算法详解与优化
文章图片存储在GitHub,网速不佳的朋友,请看<基础排序算法详解与优化> 或者 来我的技术小站 godbmw.com 1. 谈谈基础排序 常见的基础排序有选择排序.冒泡排序和插入排序.众 ...
最新文章
- Docker 和 Kubernetes 从听过到略懂:给程序员的旋风教程
- html语义化面试题,前端面试题-HTML结构语义化
- python一行代码实现99乘法表_一行代码实现九九乘法表
- 1062 最简分数(PAT乙级 C++)
- Java基础之Collection和Map
- as3调用java_关于openamf我用as3链接java程序,并调用相关的方法,但是能够连上,却不能够调用是怎么回事...
- 养心灵,才能美容颜,拥有好日子(图)
- 存储IO加强型实例I1+D1 ——为极致存储性能要求场景而生
- CentOS7安装PostgreSQL10,pgadmin4
- 互联网+(怎样保证小孩的安全) 2
- 用yum下载安装gcc
- 共享文件 麒麟系统_分享中标麒麟系统的安装教程
- 异地恋?我做了一个恋爱积分器
- 汤小丹计算机操作系统慕课版课后题答案第三章:处理机调度与死锁
- 计算机的处理器怎么看,小编教你电脑处理器怎么查看
- 【copy】也说嵌入式系统架构设计(linux 平台)
- 复旦java_复旦大学
- 测绘——通过手机和smart3D创建三维模型
- 工业物联网中的无线充电
- 基于python为Torch创建hdf5训练文件
热门文章
- UVA 1048	 Low Cost Air Travel 最短路
- 预防SQL注入攻击之我见 转
- 局域网网速太慢的问题
- [Java] 蓝桥杯 BASIC-10 基础练习 十进制转十六进制
- word 2016 for Mac 如何缩小编号与后面文字之间的缩进间隙
- 【iOS】Swift3:执行save()的时候出现:Call can throw, but it is not marked with 'try' and the error is not handl
- 【汇编】JMP跳转指令的指令长度、直接转移与间接转移、段内跳转与段间跳转
- Python获取Redis所有Key以及内容
- [性能优化]UITableView性能优化的一点感悟及计算UILabel高度的新方法 1
- NVisionXR_iOS教程十一 —— 多场景切换