Assignment
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 756 Accepted Submission(s): 400
We have N companies of troops and M missions, M>=N. One company can get only one mission. One mission can be assigned to only one company. If company i takes mission j, we can get efficiency Eij.
We have a assignment plan already, and now we want to change some companies’ missions to make the total efficiency larger. And also we want to change as less companies as possible.
1<=N<=M<=50, 1<Eij<=10000.
Your program should process to the end of file.
3 3 2 1 3 3 2 4 1 26 2 2 1 3 2 3 1 2 3 1 2 3 1 2
2 26 1 2
题意是说有n个部队和m个任务,每个任务只能由一个部队完成,并且不同的部队完成不同的任务都有一个效率值。并且一开始已经给你哪个部队做哪个任务,让你求最少改动几个部队使得获得的效率值之差(最高的效率值-一开始给你的部队匹配任务的效率值)最高。
一开始直接求出最高效率值,然后减去已经给你的部队所匹配任务的效率值,第二问就求出来了。我用match数组将求得最大的效率值后的匹配和一开始它给你的进行比较,就得出改动的部队了。但是答案错了。题目要求的是最小的部队改动数,于是参考网上的题解:
因为我们要变动最小,所以对在原计划中的边要有一些特殊照顾,使得最优匹配时,尽量优先使用原计划的边,这样变化才能是最小的且不会影响原匹配。
根据这个思想,我们可以把每条边的权值扩大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 inf 0x3f3f3f
#define M 57
using namespace std;
int g[M][M],match[M],slack[M],lx[M],ly[M],s[M];
bool visx[M],visy[M];
int n,m;
bool dfs(int cur)
{
visx[cur]=true;
for(int y=1;y<=m;y++)
{
if(visy[y])continue;
int t=lx[cur]+ly[y]-g[cur][y];
if(t==0)
{
visy[y]=true;
if(match[y]==-1||dfs(match[y]))
{
match[y]=cur;
return true;
}
}
else if(slack[y]>t)
{
slack[y]=t;
}
}
return false;
}
int KM()
{
memset(match,-1,sizeof(match));
memset(ly,0,sizeof(ly));
for(int i=1;i<=n;i++)
{
lx[i]=-inf;
for(int j=1;j<=m;j++)
if(g[i][j]>lx[i])
lx[i]=g[i][j];
}
for(int x=1;x<=n;x++)
{
for(int i=1;i<=m;i++)
slack[i]=inf;
while(true)
{
memset(visx,false,sizeof(visx));
memset(visy,false,sizeof(visy));
if(dfs(x))break;
int d=inf;
for(int i=1;i<=m;i++)
if(!visy[i]&&d>slack[i])
d=slack[i];
for(int i=1;i<=n;i++)
if(visx[i])
lx[i]-=d;
for(int i=1;i<=m;i++)
if(visy[i])
ly[i]+=d;
else
slack[i]-=d;
}
}
int ans=0;
for(int i=1;i<=m;i++)
{
if(match[i]==-1)continue;
else
{
ans+=g[match[i]][i];
}
}
return ans;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&g[i][j]);
g[i][j]*=100;
}
int a,ans=0,count,tmp=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
ans+=g[i][a];
g[i][a]+=1;
}
count=KM();
printf("%d %d\n",n-count%100,count/100-ans/100);
}
return 0;
}