[题解] uva 11383 Golden Tiger Claw (二分图最佳完美匹配)
- 传送门 -
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2378
| Root |
|
(题面见pdf)
- 题意 -
对于一个 N*N 矩阵, 每个格子有一个整数w(i,j), 对每行确定一个数row(i), 每一列确定一个数col(i), 使对于任意格子(i,j), w(i,j)<=row(i)+col(i), 所有的 row 和 col 的和要最小.
- 思路 -
发现(通过题解得知)这个 col 和 row 很像最佳匹配中的两个标杆...
我们把列数和行数视为两个集合中的点, 边权就是格点上的数.
对于任意一个点, 它和对面集合中的点连边的边权都小于两边标杆之和, 满足题意...
km算法之后所有顶标之和是最小的(大概...是因为...就是...那个...记住就行...), 据此得出答案.
反正就是个脑洞题呗...
细节见代码.
- 代码 -
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf = 0x7f7f7f7f;
const int N = 500 + 5;
int W[N][N], LX[N], LY[N];
int SLACK[N], S[N], T[N], ANS[N];
int n;
bool find(int x) {
S[x] = true;
for (int i = 1; i <= n; ++i) {
if (T[i]) continue;
int tmp = LX[x] + LY[i] - W[x][i];
if (tmp == 0) {
T[i] = true;
if (ANS[i] == 0 || find(ANS[i])) {
ANS[i] = x;
return true;
}
}
else if (tmp < SLACK[i])
SLACK[i] = tmp;
}
return false;
}
int km() {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j)
SLACK[j] = inf;
while (true) {
memset(S, 0, sizeof (S));
memset(T, 0, sizeof (T));
if (find(i)) break;
int d = inf;
for (int j = 1; j <= n; ++j) {
if (!T[j] && d > SLACK[j])
d = SLACK[j];
}
for (int j = 1; j <= n; ++j) {
if (S[j]) LX[j] -= d;
}
for (int j = 1; j <= n; ++j) {
if (T[j]) LY[j] += d;
else SLACK[j] -= d;
}
}
}
int sum = 0;
for (int i = 1; i <= n; ++i)
sum += LX[i] + LY[i];
return sum;
}
int main() {
while(~scanf("%d", &n)) {
memset(LX, 0, sizeof (LX));
memset(LY, 0, sizeof (LY));
memset(ANS, 0, sizeof (ANS));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
scanf("%d", &W[i][j]);
LX[i] = max(LX[i], W[i][j]);
}
}
int ans = km();
printf("%d", LX[1]);
for (int i = 2; i <= n; ++i)
printf(" %d", LX[i]);
printf("
");
printf("%d", LY[1]);
for (int i = 2; i <= n; ++i)
printf(" %d", LY[i]);
printf("
");
printf("%d
", ans);
}
return 0;
}