二分图的带权匹配就是求出一个匹配集合,使得集合中边的权值之和最大(最小权匹配可以转化成最大权匹配,只要对取一个大数减去当前权值,或者取反)。
注意:最大权匹配必须是在保证该匹配是完备匹配的基础上权值和最大。而完备匹配是指一个匹配它包含二分图两个点集中某一个的全集(当然也可以包括这两个全集,也就是完备匹配)。
KM算法是通过给每个顶点一个标号(我们有时称之为顶标)来把求最大权匹配的问题转化为求完备匹配的问题的。我们令二分图中X部的节点的顶标为Ai,Y部的节点的顶标为Bi。X部与Y部节点之间的权值为Wi,j,那么,在算法进行的过程中,我们必须始终保持$A_i+B_i\geq W_{i,j}$成立。因为KM算法的正确性基于以下定理:
若由二分图中所有满足Ai+Bi=Wi,j的边 (i,j)构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最大权匹配。(需要证明)
设slack[j]表示右边的点j的所有不在导出子图的边对应的lx[i]+ly[j]-w[i][j]的最小值,在find过程中,若某条边不在导出子图中就用它对相应的slack值进行更新。
交错树中,在 X 部顶点集称之为 S, 在 Y 部的顶点集称之为 T定义 slack(y)= min{ (x,y)| Lx(x)+ Ly(y)- W(x,y),x∈ S, y∉ T }这样能在寻找增广路径的时候就顺便将 slack 求出。
可以保证顶标的可行性,同时,经过这一步后,图中至少会增加一条可行边。
算法代码(@蛋丁http://www.cnblogs.com/-dante-/p/3269019.html):
bool dfs(int x)//匈牙利算法寻找x的增广路径 以x为根的M的交错树
{
int y,t;
visx[x]=true;
for(y=0;y<N;y++)
{
if(visy[y]) continue;//找增广路径的过程中不妨问已经访问过的顶点
t=lx[x]+ly[y]-g[x][y];//在相等子图中寻找匹配的增广路径
if(t==0)//当前边时候是可行边
{
visy[y]=true;
if(linky[y]==-1||dfs(linky[y]))
{
linky[y]=x;
return true;
}
}
else//因为本来就需要将一条x顶点在交错树中,y顶点不在交错树中的边扩展进交错树来
//所以只改变这些不在等子图中的边的y顶点的松弛量
{
if(slack[y]>t)
slack[y]=t;
}
}
return false;
}
//外层的匈牙利算法需要O(2)的时间,而修改顶标时由于要枚举所有的边所以也需要O(2)的时间
//所以总时间是O(4)
//引入松弛量以后改变顶标就不需要枚举每一条边,只需要枚举不在交错树中的y的松弛量,所以
//时间复杂度降为O(3)
int KM()
{
int i,j,x,d,res=0;
memset(linky,-1,sizeof(linky));
memset(lx,0,sizeof(lx));//x的顶标
memset(ly,0,sizeof(ly));//y的顶标
for(i=0;i<N;i++)
for(j=0;j<N;j++)
if(g[i][j]>lx[i])
lx[i]=g[i][j];//一开始x的顶标为所有与x相连的边中权值最大的边的权值,y的顶标为0
for(x=0;x<N;x++)
{//在匈牙利算法中从每个x出发寻找增广路,如果找到就在匹配值上加1,这是为了寻找最大匹配
//而在此处,必须找到完备匹配,所以对于每一个x中的顶点,找到其增广路就跳出,找不到的话
//就需要修改顶标值直至找到为止
for(i=0;i<N;i++)
slack[i]=INF;
while(true)
{//无限循环直至找到完备匹配
memset(visx,false,sizeof(visx));
memset(visy,false,sizeof(visy));
if(dfs(x))break;//若找到了曾广路,该点完成;若没有找到,需要修顶标,使得在dfs过程中可行边增多。
d=INF;
for(i=0;i<N;i++)
{
if(!visy[i]&&d>slack[i]))//注意是取所有不在交错树中的y顶点的松弛量的最小值作为d的值
d=slack[i];
}
for(i=0;i<N;i++)
if(visx[i]) lx[i]-=d;
for(i=0;i<N;i++)
if(visy[i]) ly[i]+=d;
else slack[i]-=d;
}
}
for(i=0;i<N;i++)
if(linky[i]!=-1) res+=g[linky[i]][i];
return res;
}
相关问题转化:
KM(Kuhn-Munkres)算法求得的最大权匹配是边权值和最大,如果我想要边权之积最大,就每条边权取自然对数,然后求最大和权匹配,求得的结果a再算出e^a就是最大积匹配。
例题:
URAL1076:http://acm.timus.ru/problem.aspx?space=1&num=1076
poj 2195 Going Home(KM) :http://poj.org/problem?id=2195
参考文章:
byvoid二分图带权匹配 KM算法与费用流模型建立:https://www.byvoid.com/zhs/blog/match-km/
Kuhn-Munkres算法:http://www.nocow.cn/index.php/Kuhn-Munkres%E7%AE%97%E6%B3%95
McFlurry:http://www.cnblogs.com/mcflurry/archive/2013/01/24/2874114.html
蛋丁:http://www.cnblogs.com/-dante-/p/3269019.html
崔添翼的求最大权二分匹配的KM算法
张文泰:http://rchardx.is-programmer.com/posts/15306.html
呆呆的人v:http://blog.sina.com.cn/s/blog_691ce2b701016reh.html 这篇文章举得例子比较详细。