二分图的最大权匹配

本文探讨了二分图的最大权匹配问题,重点介绍了KM算法,该算法通过顶点标号确保最大权匹配同时是完备匹配。算法过程中保持顶标与权值的关系,并利用交错树和slack值来寻找增广路径。通过代码示例和实例题目展示了算法的应用,并提供了多个参考资料链接以供深入学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值