[HDU2853]Assignment(KM双元限制)

来源:互联网 发布:淘宝设计属于什么行业 编辑:程序博客网 时间:2024/06/05 23:57

题目:

我是超链接

题意:

有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 1e9using 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);    }}
原创粉丝点击