虛擬賽一開始lyf就對我說這是一道匹配的題目,我一看明顯裸的最優匹配,敲完提交wrong,
題目要求改變盡量少的公司,就是如果遇到相等的權值,優先選擇跟他原來匹配的,KM匹配是按序號大小來的,如果一個公司原來匹配的序號較大,前面有權值相等的點時,KM就會選擇前面的點參加匹配。想了好長時間不知道怎麼去優先選擇原來匹配的邊,
最後想著如果把原來匹配的邊變得大一些的話,就可以,但是變大的話就會影響最優匹配的總值,而且變大的話還會影響原來比他大的權值,所以就是所有的權值都得擴大,我想到的是都*100,原來匹配的邊再加1,因為最多選50條邊,也就是最多有50個01相加,不會超過一百,得到的答案除以一百,就把加的1都去掉了,只要擴大的倍數大於n就可以,,
#include<stdio.h>
#include<string.h>
#define N 55
#define inf 0x3fffffff
int map[N][N],lx[N],ly[N],sx[N],sy[N],d[N],n,m,match[N],Kmatch[N],tch[N];
int find(int x)
{
sx[x]=1;
for(int i=1;i<=m;i++)
{
if(sy[i]==1)continue;
int temp=lx[x]+ly[i]-map[x][i];
if(temp==0)
{
sy[i]=1;
if(match[i]==-1||find(match[i])==1)
{
match[i]=x;
Kmatch[x]=i;
return 1;
}
}
else d[i]=d[i]>temp?temp:d[i];
}
return 0;
}
int KM()
{
int i,j,k,min,sum;
memset(ly,0,sizeof(ly));
memset(match,-1,sizeof(match));
memset(Kmatch,-1,sizeof(Kmatch));
for(i=1;i<=n;i++)
{
lx[i]=map[i][1];
for(j=2;j<=m;j++)
if(lx[i]<map[i][j])
lx[i]=map[i][j];
}
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
d[j]=inf;
while(1)
{
memset(sx,0,sizeof(sx));
memset(sy,0,sizeof(sy));
if(find(i)==1)break;
min=inf;
for(k=1;k<=m;k++)
if(sy[k]==0&&min>d[k])
min=d[k];
for(j=1;j<=n;j++)
if(sx[j]==1)lx[j]-=min;
for(j=1;j<=m;j++)
if(sy[j]==1)ly[j]+=min;
}
}
sum=0;
for(i=1;i<=n;i++)
sum+=map[i][Kmatch[i]];
return sum;
}
int main()
{
int i,j,k,ans,sum;
while(scanf("%d%d",&n,&m)!=-1)
{
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
map[i][j]=-inf;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
scanf("%d",&map[i][j]);
map[i][j]*=100;
}
ans=0;
for(i=1;i<=n;i++)
{
scanf("%d",&k);
tch[i]=k;
ans+=map[i][k];
map[i][k]++;
}
ans/=100;
sum=KM()/100;
k=0;
for(i=1;i<=n;i++)
if(tch[i]!=Kmatch[i])
k++;
printf("%d %d\n",k,sum-ans);
}
return 0;
}