hdu 2853

来源:互联网 发布:阿里云服务器建站助手 编辑:程序博客网 时间:2024/06/07 23:02
/*题目大意:现在有N个部队和M个任务(M>=N),每个部队完成每个任务有一点的效率,效率越高越好。但是部队已经安排了一定的计划,这时需要我们尽量用最小的变动,使得所有部队效率之和最大。求最小变动的数目和变动后和变动前效率之差。解题思路:比如现在有a边和b边,权值相同,原计划中选择的是a边,但是用km时候,可能选择的就是b边,虽然权值没变,但是变动量已经变了。这就是错误的情况。因为我们要变动最小,所以对在原计划中的边要有一些特殊照顾,使得最优匹配时,尽量优先使用原计划的边,这样变化才能是最小的且不会影响原匹配。根据这个思想,我们可以把每条边的权值扩大k倍,k要大于n。然后对原计划的边都+1。精华全在这里。我们来详细说明一下。全部边都扩大了k倍,而且k比n大,这样,我们求出的最优匹配就是k倍的最大权值,只要除以k就可以得到最大权值。实现原计划的边加1,这样,在每次选择边时,这些边就有了优势,就会优先选择这些边。假如原计划的h条边被选入了最优匹配中,这样,最优权值就是k倍的最大权值+h(原计划的每条边都+1)。但是k大于n的用意何在呢?我们发现假如原计划的边全部在匹配中,只会增加n,又n<k,所以除以k后不会影响最优匹配的最大权值之和,然后我们对k取余,就正好得到加入的原计划的边的个数。这时,我们只需要用总点数-加入的原计划的点数,就可以求得最小变动数了。*/#include<stdio.h>#include<string.h>#define inf 0x3fffffff#define N 100int n,m,link[N],s[N],mark[N],lx[N],ly[N],map[N][N];int min(int a,int b){return a<b?a:b;}int max(int a,int b){return a>b?a:b;}int find(int k){int i;s[k]=1;for(i=1;i<=m;i++){if(!mark[i]&&lx[k]+ly[i]==map[k][i]){mark[i]=1;if(link[i]==-1||find(link[i])==1){link[i]=k;return 1;}}}return 0;}void km(){int i,j,k,a;memset(ly,0,sizeof(ly));memset(link,-1,sizeof(link));for(i=1;i<=n;i++){lx[i]=map[i][1];for(j=2;j<=m;j++)lx[i]=max(lx[i],map[i][j]);}for(i=1;i<=n;i++){while(1){memset(s,0,sizeof(s));memset(mark,0,sizeof(mark));if(find(i)==1)break;else{a=inf;for(j=1;j<=n;j++){if(s[j]){for(k=1;k<=m;k++)  if(!mark[k])    a=min(a,lx[j]+ly[k]-map[j][k]);}}                for(j=1;j<=n;j++)if(s[j])  lx[j]-=a;for(j=1;j<=m;j++)if(mark[j])  ly[j]+=a;}}}}int main(){int i,j,ss,sum,b;while(scanf("%d%d",&n,&m)!=EOF){memset(map,0,sizeof(map));for(i=1;i<=n;i++){for(j=1;j<=m;j++){scanf("%d",&map[i][j]);map[i][j]*=100;}}ss=0;for(i=1;i<=n;i++){scanf("%d",&b);map[i][b]+=1;ss+=map[i][b];}km();sum=0;for(i=1;i<=m;i++){if(link[i]>=1)sum+=map[link[i]][i];}printf("%d %d\n",ss%100-sum%100,sum/100-ss/100);}return 0;}

0 0