ZOJ-2362 Beloved Sons 最大权值匹配

时间:2023-03-09 09:08:51
ZOJ-2362 Beloved Sons 最大权值匹配

题意:国王有N个儿子,现在每个儿子结婚都能够获得一定的喜悦值,王子编号为1-N,有N个女孩的编号同样为1-N,每个王子心中都有心仪的女孩,现在问如果安排,能够使得题中给定的式子和最大。

分析:其实题目中那个开根号是个烟雾弹,只要关心喜悦值的平方即可。那么对王子和女孩之间构边,边权为喜悦值的平方,对于每一个王子虚拟出一个女孩边权为0,这样是为了所有的王子都能够有女孩可以配对,以便算法能够正确的执行。

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std; const int N = ;
const int inf = 0x3f3f3f3f;
int n, m;
int like[N];
int w[N][N<<];
int match[N<<];
int lx[N], ly[N<<], slack[N<<];
int vx[N], vy[N<<];
int marry[N]; bool path(int u) {
vx[u] = ;
for (int v = ; v <= m; ++v) {
if (vy[v] || w[u][v] == -) continue;
int t = lx[u]+ly[v]-w[u][v];
if (!t) {
vy[v] = ;
if (!match[v] || path(match[v])) {
match[v] = u;
return true;
}
} else {
slack[v] = min(slack[v], t);
}
}
return false;
} void KM() {
memset(lx, 0x80, sizeof (lx));
memset(ly, , sizeof (ly));
memset(match, , sizeof (match));
memset(marry, , sizeof (marry));
for (int i = ; i <= n; ++i) {
for (int j = ; j <= m; ++j) {
if (w[i][j] != -) {
lx[i] = max(lx[i], w[i][j]);
}
}
}
for (int i = ; i <= n; ++i) {
memset(slack, 0x3f, sizeof (slack));
while () {
memset(vx, , sizeof (vx));
memset(vy, , sizeof (vy));
if (path(i)) break;
int d = inf;
for (int j = ; j <= m; ++j) {
if (!vy[j]) d = min(d, slack[j]);
}
if (d == inf) break;
for (int j = ; j <= n; ++j) {
if (vx[j]) lx[j] -= d;
}
for (int j = ; j <= m; ++j) {
if (vy[j]) ly[j] += d;
else slack[j] -= d;
}
}
}
for (int i = ; i <= m; ++i) {
if (match[i] && i <= n) {
marry[match[i]] = i;
}
}
for (int i = ; i <= n; ++i) {
printf(i == ? "%d" : " %d", marry[i]);
}
puts("");
} int main() {
int T;
scanf("%d", &T);
while (T--) {
memset(w, 0xff, sizeof (w));
scanf("%d", &n);
m = n << ;
for (int i = ; i <= n; ++i) {
scanf("%d", &like[i]);
}
int x, y;
for (int i = ; i <= n; ++i) {
scanf("%d", &x);
for (int j = ; j < x; ++j) {
scanf("%d", &y);
w[i][y] = like[i] * like[i];
}
w[i][n+i] = ;
}
KM();
}
return ;
}