HDU-2853 Assignment 最大权值匹配+简直是太神了
题意:给定一个二分图,N个点对应M个点,两两之间存在一组关系,每组关系一个权值。题目中了给定了一个匹配方案,现在要求满足这组关系中的最大的匹配权值在原方案上增长了多少?并且还要求求出在原匹配方案上改变最少多少条边能够得到这个最大匹配?
解法:该题求增加了多少匹配值非常好算,问题就是这个最少改变多少条边。前面做过一道题目使用dfs遍历出所有的最优匹配情况,果断用在这里超时,主要原因是点过多,当然还有就是最大匹配不太好剪枝。想了许久硬了没有法子。百度......
正确的解法真他妈犀利,主要思想就是增加原配边的权值,而且又不会对结果造成影响。这听起来似乎是不太可能的,但是确实有办法能够办到。首先由于顶点数最多50个,那么给所有的边都乘上55,然后再给所有的原配边都加上1,那么此时的原配边的权值相比其他边更大了,又因为我们最后求解最大权值匹配时是对结果除上55,原配边匹配再多的数量对这个结果也是于事无补,所有最后MaxValue / 55就是匹配的最大权值,而MaxValue % 55就是能够保持的最大原配边数。啊......这简直是太神了。
代码如下:
#include <cstdlib> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int INF = 0x3f3f3f3f; int N, M, ieff, eff, change; int w[55][55]; int lx[55], ly[55]; int sx[55], sy[55]; int match[55], slack[55]; int ini[55]; int path(int u) { sx[u] = 1; for (int i = 1; i <= M; ++i) { if (sy[i]) continue; int t = lx[u]+ly[i]-w[u][i]; if (!t) { sy[i] = 1; if (!match[i] || path(match[i])) { match[i] = u; return true; } } else { slack[i] = min(slack[i], t); } } return false; } int KM() { memset(match, 0, sizeof (match)); memset(lx, 0, sizeof (lx)); memset(ly, 0, sizeof (ly)); for (int i = 1; i <= N; ++i) { // 将完全匹配的点集放置在主动匹配的一边 for (int j = 1; j <= M; ++j) { lx[i] = max(lx[i], w[i][j]); } } for (int i = 1; i <= N; ++i) { memset(slack, 0x3f, sizeof (slack)); while (1) { memset(sx, 0, sizeof (sx)); memset(sy, 0, sizeof (sy)); if (path(i)) break; int d = INF; for (int j = 1; j <= M; ++j) { if (!sy[j]) d = min(d, slack[j]); } for (int j = 1; j <= M; ++j) { if (sy[j]) ly[j] += d; else slack[j] -= d; } for (int j = 1; j <= N; ++j) { if (sx[j]) lx[j] -= d; } } } int ret = 0; for (int j = 1; j <= M; ++j) { if (match[j]) { ret += w[match[j]][j]; } } change = N - ret % 55; return ret / 55; } int main() { while (scanf("%d %d", &N, &M) != EOF) { ieff = 0; for (int i = 1; i <= N; ++i) { for (int j = 1; j <= M; ++j) { scanf("%d", &w[i][j]); w[i][j] *= 55; // 扩大55倍 } } for (int i = 1; i <= N; ++i) { scanf("%d", &ini[i]); w[i][ini[i]] += 1; ieff += w[i][ini[i]]; } eff = KM(); memset(sy, 0, sizeof (sy)); printf("%d %d\n", change, eff-ieff/55); } return 0; }