完全二分图的最大权完美匹配 KM算法

这篇博客主要介绍了KM算法,用于求解完全二分图的最大权完美匹配。作者通过学习和理解KM算法,整理了相关概念,如顶点的顶标、相等子图,并解释了相等子图完美匹配与原图最大匹配的关系。文章还分享了一段代码实现和个人解析,供读者参考学习。

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

         最近在切二分匹配的题目,以前只学了点概念,趁现在有时间仔细得学下,下午和晚上花了几个小时学了KM算法,开始比较难理解,看了lrj的ppt后,有点明白了,现在整理下,以便记住。

        概念:

                 lx(u),ly(v)分别表示左右两边的顶点顶标,即每一个点的值,这里有lx(u)+ly(v)>=w(u,v),选值时可令ly(v)=0,lx(u)为邻边权值最大的一个值。

                相等子图:G的生成子图,仅包含满足lx(u)+ly(v)==w(u,v)的边(所以叫相等,我是这样理解的)

                定理:如果相等子图有完美匹配,则该匹配是原图的最大匹配。(证明可以去搜刘汝佳的ppt)

                思想:选定顶点的顶标,寻找相等子图的完美匹配,若找到,则终止算法,否则更新顶标,使更多的边满足lx(u)+ly(v)==w(u,v);循环直到算法终止。

                一下是一个在网上找的代码以及个人的解析分享下(若有错误,谢谢指出):

               

#define INF 0x3f3f3f
cosnt int Max =1024;
int n;   //顶点数
int w[Max][Max];
int lx[Max],ly[Max];
bool sx[Max],sy[Max];  //标记x,y
int mat[Max];          //匹配
bool path(int u)       //寻找u的匹配,找增广路,基本和匈牙利的差不多
{
   sx[u]=true;
   for(int v=0;v<n;v++)
       if(!sy[v]&&lx[u]+ly[v]==w[u][v])    //判断条件,是否满足lx[u]+ly[v]=w[u][v]
        {
            sy[v]=true;
            if(mat[v]==-1||path(mat[v]))
             {
                  mat[v]=u;
                  return true;
             }
        }
   return false;
}
int KM(bool maxsum=true)   //maxsum==true为最大权值,否则为最小
{
   int i,j;
   if(!maxsum)             //由maxsum进行对边权修改
   {
       for(i=0;i<n;i++)
          for(j=0;j<n;j++)
		w[i][j]*=-1;
   }
   for(i=0;i<n;i++)       //初始化lx,ly
   {
  	lx[i]=-INF;
	ly[i]=0;
        for(j=0;j<n;j++)
          lx[i]=max(w[i][j],lx[i]);	
   }
   memset(mat,-1,sizeof(mat));
   for(int u=0;u<n;u++)
      while(1)
      {
	memset(sx,0,sizeof(sx));
        memset(sy,0,sizeof(sy));
	if(path(u)) break;       //若找到匹配则跳出,判断下一个点
        int dx=INF;
	for(i=0;i<n;i++)         //对访问过的点不满足lx[u]+ly[v]=w[u][v]的点对,找出最小差值dx
            if(sx[i])
            for(j=0;j<n;j++)
 	       if(!sy[j])
 		dx=min(lx[i]+ly[j]-w[i][j],dx);
        for(i=0;i<n;i++)   
        {
	    if(sx[i])           //对访问过的x更新,使满足差值dx的点更新后满足lx[i]+ly[i]==w[i][j],从而增加可行边
	       lx[i]-=dx;
	    if(sy[i])           //对访问过的y更新,使得已经满足条件的lx[i]+ly[i]==w[i][j]依然成立
	       ly[i]+=dx;
	}
      }
     int sum=0;
     for(i=0;i<n;i++)
        sum+=w[mat[i]][i];
     if(!maxsum)    //若有必要,进行恢复
    {
       sum=-sum;
       for(i=0;i<n;i++)
          for(j=0;j<n;j++)
		w[i][j]*=-1;
    }	
    return sum;
}

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值