题目:
题意:
有n个人和m个任务,每一个人要分配到一个任务,每一个人分配到一个任务会有一个价值。
现已知道一个初始的分配任务的方案,要求对这种方案进行调整,使在价值最大的前提下,每个人的任务修改的个数最少。
题解:
这道题目有两个限制,我们可以考虑这种问题的一种常见解法
双元限制讲解:
一般来说,如果有两个变量v1和v2需要优化,
要求首先满足v1最大,在v1相同的情况下v2最小,
这个时候我们一般会把这两个变量合成一个量:
M*v1+v2
(其中M是一个很大的量)
当X=M*v1+v2取最大值的时候
v1=X/M
v2=X%M
准确的说,M是一个比“v2的最大理论值和v2的最小理论值之差”还要大的数
这样,只要两个解的v1不同,则不管v2差多少,都是v1起决定性作用
只有v1相同时,才取决于v2
那么对于这道题目来说,M取100(>50)就好了(因为最多有50条边被钦定),然后给每一个“钦定”的任务边权+1,这样可以保证即使跑出来的最大值一开始会一样,但这个+1就很有用啦
因为是人-任务一一对应,用KM求出X的值,那么总价值v1,修改的边n-v2
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#define INF 1e9
using namespace std;
int n,m,e[55][55],vis[200],belong[55],delta[55],lx[55],ly[55];
bool find(int i,int id)
{
vis[i]=id;
for (int j=1;j<=m;j++)
if (vis[j+n]!=id)
{
if (lx[i]+ly[j]==e[i][j])
{
vis[j+n]=id;
if (!belong[j] || find(belong[j],id))
{
belong[j]=i;
return 1;
}
}else delta[j]=min(delta[j],lx[i]+ly[j]-e[i][j]);
}
return 0;
}
int KM()
{
int i,j,ans=0,id=0,ii;
memset(vis,0,sizeof(vis));
memset(lx,0,sizeof(lx));
memset(belong,0,sizeof(belong));
memset(ly,0,sizeof(ly));
for (i=1;i<=n;i++)
for (j=1;j<=m;j++) lx[i]=max(lx[i],e[i][j]);
for (ii=1;ii<=n;ii++)
{
for (j=1;j<=m;j++) delta[j]=INF;
while (1)
{
id++;
if (find(ii,id)) break;
int a=INF;
for (i=1;i<=m;i++)
if (vis[i+n]!=id) a=min(delta[i],a);//注意这里只有没访问过的才更新a
for (i=1;i<=n;i++)
if (vis[i]==id) lx[i]-=a;
for (i=1;i<=m;i++)
if (vis[i+n]==id) ly[i]+=a;
else delta[i]-=a;
}
}
for (i=1;i<=m;i++)
if (belong[i]) ans+=e[belong[i]][i];
return ans;
}
int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
int use=0,i,j;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++) scanf("%d",&e[i][j]),e[i][j]*=100;
for (i=1;i<=n;i++)
{
int x;scanf("%d",&x);
use+=e[i][x]/100; e[i][x]++;
}
int ans=KM();
printf("%d %d\n",n-ans%100,ans/100-use);
}
}