题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2853

Last year a terrible earthquake attacked Sichuan province. About 300,000 PLA soldiers attended the rescue, also ALPCs. Our mission is to solve difficulty problems to optimization the assignment of troops. The assignment is measure by efficiency, which is an integer, and the larger the better.
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.
 
题目描述:n个组和m个任务,Eij表示第i个组完成第j个任务的效率,每个组只能完成一个任务,每个任务只能由一个组完成,目前已经有了一个计划,但是现在我们想要让总效率达到最大,并且在此前提下还需要改变重新分配任务的组的个数最少。求出最大效率减去原先计划的效率和重新分配任务的组的个数。
 
算法分析:这道题的思维方式的确很独特,也很巧妙。首先解决第一个问题:最大效率减去原先计划的效率的差值。最大效率很好解决,用KM算法即可,原先计划的效率直接根据输入统计即可。那么第二个问题呢?重新分配任务的组的最小个数。
方法一:首先为了保证在最大效率情况下尽量选择原先已经分配了的任务,所以我们可以对原先已经分配了的任务在效率上加1,这样即使两个组对同一个任务效率相同也会选择原先的计划,然后我们标记一下有哪些边是原先计划里的。剩下的就是KM了。
说明:这种方法为什么会WA呢,还没有找到原因, 关键在于对每条边权都要乘以一个k(k>n),下面的代码就没有乘以k,想想应该是这种方法下求得的不是最大效率吧,但为什么不是最大效率呢? 每条边都乘以k最后的最大效率再除以k,和直接求得的最大效率不是一样的吗?
若有大牛明白其中奥妙,还望指点一二,在此感谢。
 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define inf 0x7fffffff
using namespace std;
const int maxn=; int n,m,k,sum;
int lx[maxn],ly[maxn],visx[maxn],visy[maxn];
int link[maxn],slack[maxn],w[maxn][maxn];
int vis[maxn][maxn]; int dfs(int x)
{
visx[x]=;
for (int y= ;y<=m ;y++)
{
if (visy[y]) continue;
int t=lx[x]+ly[y]-w[x][y];
if (t==)
{
visy[y]=;
if (link[y]==- || dfs(link[y]))
{
link[y]=x;
return ;
}
}
else if (slack[y]>t) slack[y]=t;
}
return ;
} void KM()
{
memset(link,-,sizeof(link));
memset(ly,,sizeof(ly));
for (int i= ;i<=n ;i++)
{
lx[i]=-inf;
for (int j= ;j<=m ;j++)
lx[i]=max(lx[i],w[i][j]);
}
for (int x= ;x<=n ;x++)
{
for (int i= ;i<=m ;i++) slack[i]=inf;
while ()
{
memset(visx,,sizeof(visx));
memset(visy,,sizeof(visy));
if (dfs(x)) break;
int d=inf;
for (int i= ;i<=m ;i++)
{
if (!visy[i] && slack[i]<d) d=slack[i];
}
for (int i= ;i<=n ;i++)
if (visx[i]) lx[i] -= d;
for (int i= ;i<=m ;i++)
{
if (visy[i]) ly[i] += d;
else slack[i] -= d;
}
}
}
int ans=,cnt=;
for (int i= ;i<=m ;i++)
{
if (link[i]!=-)
{
ans += w[link[i] ][i];
if (vis[link[i] ][i]) cnt++;
}
}
printf("%d %d\n",n-cnt,ans-sum-cnt);
// for (int i=1 ;i<=m ;i++)
// {
// if (link[i]!=-1) ans += w[link[i] ][i];
// }
// printf("%d %d\n",n-ans%k,ans/k-sum);
} int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
memset(w,,sizeof(w));
memset(vis,,sizeof(vis));
k=;
for (int i= ;i<=n ;i++)
{
for (int j= ;j<=m ;j++)
{
scanf("%d",&w[i][j]);
/// w[i][j] *= k;
}
}
int a;
sum=;
for (int i= ;i<=n ;i++)
{
scanf("%d",&a);
sum += w[i][a];
///sum += w[i][a]/k;
w[i][a] ++ ;
vis[i][a]=;
}
KM();
}
return ;
}

方法二:和方法一的区别就在于对每条边都乘以k(比如k=200),对于原有匹配w[x][y]++,最后的答案最大效率为ans。

那么差值=ans/k-sum;个数=n-ans%k。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define inf 0x7fffffff
using namespace std;
const int maxn=; int n,m,k,sum;
int lx[maxn],ly[maxn],visx[maxn],visy[maxn];
int link[maxn],slack[maxn],w[maxn][maxn]; int dfs(int x)
{
visx[x]=;
for (int y= ;y<=m ;y++)
{
if (visy[y]) continue;
int t=lx[x]+ly[y]-w[x][y];
if (t==)
{
visy[y]=;
if (link[y]==- || dfs(link[y]))
{
link[y]=x;
return ;
}
}
else if (slack[y]>t) slack[y]=t;
}
return ;
} void KM()
{
memset(link,-,sizeof(link));
memset(ly,,sizeof(ly));
for (int i= ;i<=n ;i++)
{
lx[i]=-inf;
for (int j= ;j<=m ;j++)
lx[i]=max(lx[i],w[i][j]);
}
for (int x= ;x<=n ;x++)
{
for (int i= ;i<=m ;i++) slack[i]=inf;
while ()
{
memset(visx,,sizeof(visx));
memset(visy,,sizeof(visy));
if (dfs(x)) break;
int d=inf;
for (int i= ;i<=m ;i++)
{
if (!visy[i] && slack[i]<d) d=slack[i];
}
for (int i= ;i<=n ;i++)
if (visx[i]) lx[i] -= d;
for (int i= ;i<=m ;i++)
{
if (visy[i]) ly[i] += d;
else slack[i] -= d;
}
}
}
int ans=,cnt=;
for (int i= ;i<=m ;i++)
{
if (link[i]!=-) ans += w[link[i] ][i];
}
printf("%d %d\n",n-ans%k,ans/k-sum);
} int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
memset(w,,sizeof(w));
k=;
for (int i= ;i<=n ;i++)
{
for (int j= ;j<=m ;j++)
{
scanf("%d",&w[i][j]);
w[i][j] *= k;
}
}
int a;
sum=;
for (int i= ;i<=n ;i++)
{
scanf("%d",&a);
sum += w[i][a]/k;
w[i][a] ++ ;
}
KM();
}
return ;
}

hdu 2853 Assignment KM算法的更多相关文章

  1. 【HDU 2853】 KM算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2853 题意:有n个公司,m个任务,每个公司做每个任务都有一个效率值,最开始每个公司都指派了一个任务,现 ...

  2. HDU 2853 Assignment(KM最大匹配好题)

    HDU 2853 Assignment 题目链接 题意:如今有N个部队和M个任务(M>=N),每一个部队完毕每一个任务有一点的效率,效率越高越好.可是部队已经安排了一定的计划,这时须要我们尽量用 ...

  3. HDU(2255),KM算法,最大权匹配

    题目链接 奔小康赚大钱 Time Limit: 1000/1000MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Su ...

  4. HDU 2853 最大匹配&KM模板

    http://acm.hdu.edu.cn/showproblem.php?pid=2853 这道题初看了没有思路,一直想的用网络流如何解决 参考了潘大神牌题解才懂的 最大匹配问题KM 还需要一些技巧 ...

  5. HDU 2853 & 剩余系+KM模板

    题意: 给你一张二分图,给一个原匹配,求原匹配改动最少的边数使其边权和最大. SOL: 我觉得我的智商还是去搞搞文化课吧..这种题给我独立做我大概只能在暴力优化上下功夫.. 这题的处理方法让我想到了剩 ...

  6. 【HDU 2853】Assignment (KM)

    Assignment Problem Description Last year a terrible earthquake attacked Sichuan province. About 300, ...

  7. Assignment (HDU 2853 最大权匹配KM)

    Assignment Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  8. Assignment HDU - 2853(二分图匹配 KM 新边旧边)

    传送门: Assignment HDU - 2853 题意:题意直接那松神的题意了.给了你n个公司和m个任务,然后给你了每个公司处理每个任务的效率.然后他已经给你了每个公司的分配方案,让你求出最多能增 ...

  9. hdu 2426 Interesting Housing Problem 最大权匹配KM算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2426 For any school, it is hard to find a feasible ac ...

随机推荐

  1. webservice安全性之 SoapHeader自定义身份验证

    相信很多开发者都用过WebService来实现程序的面向服务,本文主要介绍WebService的身份识别实现方式,当然本文会提供一个不是很完善的例子,权当抱砖引玉了. 首先我们来介绍webservic ...

  2. 傅里叶变换 fft_generic halcon

    傅立叶变换(FT, Fourier Transform)的作用是将一个信号由时域变换到频域.其实就是把数据由横坐标时间.纵坐标采样值的波形图格式,转换为横坐标频率.纵坐标振幅(或相位)的频谱格式.变换 ...

  3. 【代码】ini 文件读取工具类

    using System; using System.Runtime.InteropServices; using System.Text; namespace hrattendance.Common ...

  4. 【转】Linux Soclet编程

    原文地址:http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编 ...

  5. <?php>慢慢写一些php的cookie问题<?>

    写网站是个爬坑的过程,在你设计完功能之后,就会发现:卧槽,这个怎么实现?你妹,这个能实现么? 进了公司分工明确还好说(= =学长们都这么说),在学校自己没事写一些项目的话只能自己爬坑了. 蹬蹬瞪蹬,登 ...

  6. wpf 在引用外部的资源字典

    启动的APP.xaml

  7. python 删除list中重复元素

    list = [1,1,3,4,6,3,7] 1. for s in list: if list.count(s) >1: list.remove(s) 2. list2=[] for s in ...

  8. wordpress 为文章内容添加自动过滤,例如为出站链接添加nofollow,也可以将淘宝客链接转换。。

    做seo的都明白,反向链接对与网站的优化有着很重要的作用,是搜索引擎给网站排名的一个重要因素.为了添加反向链接,SEO作弊者会在论坛和博客等大量发布带无关链接的 内容.这些垃圾链接的存在给搜索引擎对网 ...

  9. .NET基础:修饰符

    访问修饰符 软道语录定义: 访问修饰符就是类,属性和方法的电影分级制度 . public:访问不受限制. protected:访问仅限于包含类或从包含类派生的类型.只有包含该成员的类以及继承的类可以存 ...

  10. QT 按钮类继承处理带定时器

    01.class KeyButton : public QPushButton  02.{  03.    Q_OBJECT  04.public:  05.    explicit KeyButto ...