HDU 2853 Assignment(最大完备匹配)
解题思路
这题的思路十分巧妙。首先,如果有多条等权值的路径的话,需要优先选择已经选好的那些路径,我们可以把每条边的边权都扩大k倍再加1,这样以来,选好的和没选的等边权的边就能区分开来,而且也不会影响不同边权的边的关系。
第二个问题就是怎么求改动的次数了,我们把原来的边权扩大k倍,只有选过的边才加1,那么只要最优的选择含有一条原先选过的边,那么结果模k就会多1,用n减去最大边权模k的结果就是改动的次数,注意这个k要比n大,不然取模的时候就取不满0~n了。
代码
const int maxn = 1e2+10;
const int maxm = 30;
int match[maxn], lx[maxn], ly[maxn], s[maxn];
int visx[maxn], visy[maxn], pre[maxn];
int n, m, g[maxn][maxn];
void find(int k) {
int y = 0, p = 0; clr(pre, 0);
for (int i = 1; i<=m; ++i) s[i] = INF;
match[y] = k;
while(1) {
int x = match[y], d = INF; visy[y] = 1;
for (int i = 1; i<=m; ++i)
if (!visy[i]) {
if (s[i] > lx[x]+ly[i]-g[x][i])
s[i] = lx[x]+ly[i]-g[x][i], pre[i] = y;
if (d > s[i]) d = s[i], p = i;
}
for (int i = 0; i<=m; ++i) {
if (visy[i]) lx[match[i]] -= d, ly[i] += d;
else s[i] -= d;
}
y = p;
if (!match[y]) break;
}
while(y) match[y] = match[pre[y]], y = pre[y];
}
int km() {
clr(lx, 0x3f); clr(ly, 0); clr(match, 0);
for (int i = 1; i<=n; ++i)
clr(visy, 0), find(i);
int ans = 0;
for (int i = 1; i<=m; ++i) ans += g[match[i]][i];
return ans;
}
int main() {
while(~scanf("%d%d", &n, &m)) {
for (int i = 1; i<=n; ++i)
for (int j = 1; j<=m; ++j)
scanf("%d", &g[i][j]), g[i][j] *= 100;
int sum = 0;
for (int i = 1, j; i<=n; ++i)
scanf("%d", &j), sum += g[i][j], ++g[i][j];
int res = km();
printf("%d %d\n", n-res%100, (res-sum)/100);
}
return 0;
}