hdu2853 Assignment (KM最少改动边)

来源:互联网 发布:众安保险尊享e生 知乎 编辑:程序博客网 时间:2024/06/05 14:42

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2853


题解:把每条边的权值扩大k倍(k>n),然后属于原始任务的边权值+1,权值加1是为了当两条边权值相同时,更优先选择属于原始任务的边。假如原任务的h条边被选入了最优匹配中,最优权值就是k倍的最大权值+k(原计划的每条边都+1)。假如原计划的边全部在匹配中,只会增加n,又n<k, 所以除以k后不会影响最优匹配的最大权值之和,对k取余,就正好得到加入的原始任务的边的个数。只需要用总点数-加入的原始任务的点数,就可以求得最小变动数了。 


#include <stdio.h>      #include <string.h>   #define INF 0x3f3f3f3f      #define MAXN 52      int w[MAXN][MAXN],match[MAXN];      int lx[MAXN],ly[MAXN],slack[MAXN];      int visitx[MAXN],visity[MAXN];    int nx,ny;  int find(int x)  {    int i,temp;    visitx[x]=1;    for(i=1;i<=ny;++i)    {    if(visity[i])    continue;    temp=lx[x]+ly[i]-w[x][i];    if(temp==0)    {    visity[i]=1;    if(match[i]==-1||find(match[i]))    {    match[i]=x;    return 1;    }    }    else if(slack[i]>temp)    {    slack[i]=temp;    }    }    return 0;    }    int KM()    {    int i,j,d,ans;    memset(ly,0,sizeof(ly));    memset(match,-1,sizeof(match));    for(i=1;i<=nx;++i)    {//lx初始化为与它关联边中最大的    lx[i]=w[i][1];    for(j=2;j<=ny;++j)    if(w[i][j]>lx[i])    lx[i]=w[i][j];    }    for(i=1;i<=nx;++i)    {    for(j=1;j<=ny;++j)    slack[j]=INF;    while(1)    {    memset(visitx,0,sizeof(visitx));    memset(visity,0,sizeof(visity));    if(find(i))    break;    d=INF;    for(j=1;j<=ny;++j)    {    if(!visity[j]&&d>slack[j])    d=slack[j];    }    for(j=1;j<=nx;++j)    {    if(visitx[j])    lx[j]-=d;    }    for(j=1;j<=ny;++j)    {    if(visity[j])    ly[j]+=d;    else    slack[j]-=d;    }    }    }    ans=0;      for(i=1;i<=ny;++i)    {    if(match[i]!=-1)     ans+=w[match[i]][i];   }return ans;  }  int main(){int i,j,ans,val,sum;while(scanf("%d %d",&nx,&ny)!=EOF){for(i=1;i<=nx;++i){for(j=1;j<=ny;++j){scanf("%d",&val);w[i][j]=val*100;}}sum=0;for(i=1;i<=nx;++i){scanf("%d",&val);sum+=w[i][val];w[i][val]++;}ans=KM();printf("%d %d\n",nx-ans%100,ans/100-sum/100);}return 0;}


原创粉丝点击