二分图的最大权匹配

来源:互联网 发布:mac进入屏保快捷键 编辑:程序博客网 时间:2024/06/04 18:46

二分图的带权匹配就是求出一个匹配集合,使得集合中边的权值之和最大(最小权匹配可以转化成最大权匹配,只要对取一个大数减去当前权值,或者取反)。

注意:最大权匹配必须是在保证该匹配是完备匹配的基础上权值和最大。而完备匹配是指一个匹配它包含二分图两个点集中某一个的全集(当然也可以包括这两个全集,也就是完备匹配)。

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 这篇文章举得例子比较详细。


原创粉丝点击