HDU 2853 Assignment(KM匹配)

来源:互联网 发布:汇川plc编程软件安装 编辑:程序博客网 时间:2024/06/06 01:01
题目链接:  http://acm.hdu.edu.cn/showproblem.php?pid=2853

 

因为我们要变动最小,所以对在原计划中的边要有一些特殊照顾,使得最优匹配时,尽量优先使用原计划的边,这样变化才能是最小的且不会影响原匹配。

根据这个思想,我们可以把每条边的权值扩大k倍,k要大于n。然后对原计划的边都+1。精华全在这里。我们来详细说明一下。

全部边都扩大了k倍,而且k比n大,这样,我们求出的最优匹配就是k倍的最大权值,只要除以k就可以得到最大权值。实现原计划的边加1,这样,在每次选择边时,这些变就有了优势,就会优先选择这些边。假如原计划的h条边被选入了最优匹配中,这样,最优权值就是k倍的最大权值+k(原计划的每条边都+1)。但是k大于n的用意何在呢?我们发现假如原计划的边全部在匹配中,只会增加n,又n<k,所以除以k后不会影响最优匹配的最大权值之和,然后我们对k取余,就正好得到加入的原计划的边的个数。这时,我们只需要用总点数-加入的原计划的点数,就可以求得最小变动数了。

思路果然巧妙,既然自己想不出来,就见一个记住一个吧。积少成多。

 

代码:

#include<stdio.h>
#include<string.h>
#define MAX 55

intmap[MAX][MAX],match[MAX],m,n;
intsx[MAX],sy[MAX],lx[MAX],ly[MAX];

intMatch(int u)
{
   int j;
   sx[u]=1;
   for(j=1;j<=m;j++)
      if(!sy[j]&&map[u][j]==lx[u]+ly[j])
      {
         sy[j]=1;
         if(!match[j]|| Match(match[j]))
         {
             match[j]=u;
             return 1;
         }
      }
      return 0;
}

voidKM_Match()
{
   int i,j,k,min;
   for(i=1;i<=n;i++)
   {
      lx[i]=map[i][1];
      for(j=2;j<=m;j++)
         lx[i]=lx[i]>map[i][j]?lx[i]:map[i][j];
   }
   memset(ly,0,sizeof(ly));
   memset(match,0,sizeof(match));
   for(i=1;i<=n;i++)
   {
      while(1)
      {
         memset(sx,0,sizeof(sx));
         memset(sy,0,sizeof(sy));
         if(Match(i))break;
         min=0xfffffff;
         for(j=1;j<=n;j++)
         {
             if(sx[j])
             {
                for(k=1;k<=m;k++)
                   if(!sy[k])
                       min=min>(lx[j]+ly[k]-map[j][k])?(lx[j]+ly[k]-map[j][k]):min;
             }
         }
         for(j=1;j<=n;j++)if(sx[j])lx[j]-=min;
         for(j=1;j<=m;j++)if(sy[j])ly[j]+=min;
      }
   }
}


intmain()
{
   int i,j,sum,res;
   while(scanf("%d%d",&n,&m)!=EOF)
   {
      res=0;
      for(i=1;i<=n;i++)
         for(j=1;j<=m;j++)
         {
             scanf("%d",&map[i][j]);
             map[i][j]*=100;
         }
      for(i=1;i<=n;i++)
      {
         scanf("%d",&match[i]);
         map[i][match[i]]++;
         res+=map[i][match[i]];
      }
      KM_Match();
      sum=0;
      for(i=1;i<=m;i++)
         if(match[i])sum+=map[match[i]][i];
      printf("%d%d\n",res%100-sum%100,sum/100-res/100);
   }
   return 0;
}

原创粉丝点击