二分图最优完美匹配

先给出模板:

#include<cstdio>  
#include<cstring>  
#include<algorithm>  
using namespace std;  
const int maxn=100+5;  
  
//把W[maxn][maxn]数组的内容读进去之后,调用solve(n)即可计算出二分图最优匹配  
//不过需要保证该图肯定有完美匹配  
//因为本图用的W[][]来表示一个完全图,所以一定存在完美匹配的  
struct Max_Match  
{  
    int W[maxn][maxn],n;  //W是权值矩阵,n为左右点集大小  
    int Lx[maxn],Ly[maxn];//左右点集的可行顶标值  
    bool S[maxn],T[maxn]; //标记左右点集是否已被访问过  
    int left[maxn];       //left[i]=j表右i与左j匹配,为-1时表无匹配  
  
    bool match(int i)  
    {  
        S[i]=true;  
        for(int j=1;j<=n;j++)if(Lx[i]+Ly[j]==W[i][j] && !T[j])  
        {  
            T[j]=true;  
            if(left[j]==-1 || match(left[j]))  
            {  
                left[j]=i;  
                return true;  
            }  
        }  
        return false;  
    }  
  
    //更新可行顶标,纳入更多的边进来  
    void update()  
    {  
        int a=1<<30;  
        for(int i=1;i<=n;i++)if(S[i])  
        for(int j=1;j<=n;j++)if(!T[j])  
        {  
            a = min(a,Lx[i]+Ly[j]-W[i][j]);  
        }  
        for(int i=1;i<=n;i++)  
        {  
            if(S[i]) Lx[i]-=a;  
            if(T[i]) Ly[i]+=a;  
        }  
    }  
  
    int solve(int n)  
    {  
        this->n=n;  
        memset(left,-1,sizeof(left));  
        for(int i=1;i<=n;i++)//初始化可行顶标值  
        {  
            Lx[i]=Ly[i]=0;  
            for(int j=1;j<=n;j++)  
                Lx[i]=max(Lx[i], W[i][j]);  
        }  
  
        for(int i=1;i<=n;i++)  
        {  
            while(true)  
            {  
                for(int j=1;j<=n;j++) S[j]=T[j]=false;  
                if(match(i)) break;  
                else update();  
            }  
        }  
  
        int ans=0;//最优完美匹配的权值  
        for(int i=1;i<=n;i++) ans+= W[left[i]][i];  
        return ans;  
    }  
}KM;  

最大权二分匹配问题就是给二分图的每条边一个权值,选择若干不相交的边,得到的总权值最大。解决这个问题可以用KM算法。理解KM算法需要首先理解“可行顶标”的概念。可行顶标是指关于二分图两边的每个点的一个值lx[i]或ly[j],保证对于每条边w[i][j]都有lx[i]+ly[j]-w[i][j]>=0。如果所有满足lx[i]+ly[j]==w[i][j]的边组成的导出子图中存在一个完美匹配,那么这个完美匹配肯定就是原图中的最大权匹配。理由很简单:这个匹配的权值之和恰等于所有顶标的和,由于上面的那个不等式,另外的任何匹配方案的权值和都不会大于所有顶标的和。

但问题是,对于当前的顶标的导出子图并不一定存在完美匹配。这时,可以用某种方法对顶标进行调整。调整的方法是:根据最后一次不成功的寻找交错路的DFS,取所有i被访问到而j没被访问到的边(i,j)的lx[i]+ly[j]-w[i][j]的最小值d。将交错树中的所有左端点的顶标减小d,右端点的顶标增加d。经过这样的调整以后:原本在导出子图里面的边,两边的顶标都变了,不等式的等号仍然成立,仍然在导出子图里面;原本不在导出子图里面的边,它的左端点的顶标减小了,右端点的顶标没有变,而且由于d的定义,不等式仍然成立,所以他就可能进入了导出子图里。

初始时随便指定一个可行顶标,比如说lx[i]=max{w[i][j]|j是右边的点},ly[i]=0。然后对每个顶点进行类似Hungary算法的find过程,如果某次find没有成功,则按照这次find访问到的点对可行顶标进行上述调整。这样就可以逐步找到完美匹配了。

值得注意的一点是,按照上述d的定义去求d的话需要O(N^2)的时间,因为d需要被求O(N^2)次,这就成了算法的瓶颈。可以这样优化:设slack[j]表示右边的点j的所有不在导出子图的边对应的lx[i]+ly[j]-w[i][j]的最小值,在find过程中,若某条边不在导出子图中就用它对相应的slack值进行更新。然后求d只要用O(N)的时间找到slack中的最小值就可以了。

[KM算法的几种转化]

KM算法是求最大权完备匹配,如果要求最小权完备匹配怎么办?方法很简单,只需将所有的边权值取其相反数,求最大权完备匹配,匹配的值再取相反数即可。

KM算法的运行要求是必须存在一个完备匹配,如果求一个最大权匹配(不一定完备)该如何办?依然很简单,把不存在的边权值赋为0。

KM算法求得的最大权匹配是边权值和最大,如果我想要边权之积最大,又怎样转化?还是不难办到,每条边权取自然对数,然后求最大和权匹配,求得的结果a再算出e^a就是最大积匹配。至于精度问题则没有更好的办法了。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值