HDU-2853 Assignment【二分图最优匹配】

来源:互联网 发布:java工程师能做什么 编辑:程序博客网 时间:2024/06/05 23:47

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

题目大意:

现在有N个部队和M个任务(M>=N),每个部队完成每个任务有一点的效率,效率越高越好。但是部队已经安排了一定的计划,这时需要我们尽量用最小的变动,使得所有部队效率之和最大。求最小变动的数目和变动后和变动前效率之差。

解题思路:

这道题刚开始思路很清晰:求出变动前的效率,然后求出变动后的效率。作差就是第二个答案。用一个finish数组记录变动前每个任务匹配的部队,match记录最优情况下每个任务对应的部队。然后遍历2个数组,统计变化量即可。

感觉良好,一交,WA,是不是什么写错了?检查没错,忘初始化了?检查,还没错。就郁闷了。。

百度之,发现都是另外一种思路。无语呀。。。。。

然后仔细想之后发现这种思路是有问题的,比如现在有a边和b边,权值相同,原计划中选择的是a边,但是用km时候,可能选择的就是b边,虽然权值没变,但是变动量已经变了。这就是错误的情况。想想可以用一个数组标记已经匹配的边,然后等权值相同时先看看有没有在原匹配中出现过的权值相同的边,但是实现了一下,总是很繁琐。

So,就用网上的思路A了这道题。。鄙视

巧妙的思路:

因为我们要变动最小,所以对在原计划中的边要有一些特殊照顾,使得最优匹配时,尽量优先使用原计划的边,这样变化才能是最小的且不会影响原匹配。

根据这个思想,我们可以把每条边的权值扩大k倍,k要大于n。然后对原计划的边都+1。精华全在这里。我们来详细说明一下。

全部边都扩大了k倍,而且k比n大,这样,我们求出的最优匹配就是k倍的最大权值,只要除以k就可以得到最大权值。实现原计划的边加1,这样,在每次选择边时,这些变就 有了优势,就会优先选择这些边。假如原计划的h条边被选入了最优匹配中,这样,最优权值就是k倍的最大权值+k(原计划的每条边都+1)。但是k大于n的用意何在呢?我们发现假如原计划的边全部在匹配中,只会增加n,又n<k,所以除以k后不会影响最优匹配的最大权值之和,然后我们对k取余,就正好得到加入的原计划的边的个数。这时,我们只需要用总点数-加入的原计划的点数,就可以求得最小变动数了。

思路果然巧妙,既然自己想不出来,就见一个记住一个吧。积少成多。

代码如下:

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;#define N 55#define MAX 1<<28#define CLR(arr, what) memset(arr, what, sizeof(arr))int map[N][N];int lx[N], ly[N], slack[N];bool visitx[N], visity[N];int match[N];int n, m;bool Hungary(int u){int temp;visitx[u] = true;for(int i = 1; i <= m; ++i){if(visity[i])continue;temp = lx[u] + ly[i] - map[u][i];if(temp == 0){visity[i] = true;if(match[i] == - 1 || Hungary(match[i])){match[i] = u;return true;}}elseslack[i] = min(slack[i], temp);}return false;}bool KM_Perfect_Match(){int temp;CLR(lx, 0);CLR(ly, 0);for(int i = 1; i <= n; ++i)for(int j = 1; j <= m; ++j)lx[i] = max(lx[i], map[i][j]);for(int i = 1; i <= n; ++i){for(int j = 1; j <= m; ++j)slack[j] = MAX;while(1){CLR(visitx, false);CLR(visity, false);if(Hungary(i))break;else{temp = MAX;for(int j = 1; j <= m; ++j)if(!visity[j])temp = min(temp, slack[j]);if(temp == MAX)return false;for(int j = 1; j <= n; ++j)if(visitx[j])lx[j] -= temp;for(int j = 1; j <= m; ++j)if(visity[j])ly[j] += temp;elseslack[j] -= temp;}}}return true;}int main(){int finish;int ans, res; //res原计划总权值,ans最优权值while(~scanf("%d%d", &n, &m)){res = ans = 0;CLR(match, -1);CLR(map, 0);for(int i = 1; i <= n; ++i)for(int j = 1; j <= m; ++j){scanf("%d", &map[i][j]);map[i][j] *= 100; //极其巧妙的建图}for(int i = 1; i <= n; ++i){scanf("%d", &finish);res += map[i][finish];map[i][finish] += 1; //极其巧妙的建图}KM_Perfect_Match();for(int i = 1; i <= m; ++i){if(match[i] != -1)ans += map[ match[i] ][i];}printf("%d %d\n", n - ans % 100, ans / 100 - res / 100);}return 0;}

原创粉丝点击