http://acm.hdu.edu.cn/showproblem.php?pid=2853
【题意】给出一个原匹配,问最少需要修改多少条边才能变成最大匹配
【思路】重点在于数据范围,n<=m
可知此一定是完备匹配
为了让给定原匹配中的边优先选,我们对边权做以下操作
w[i][j]=e[i][j]*(n+1)+(j==x) ->是否是原边
这样,即时全部使用原边也不会对答案产生影响
最后使用原边数:ans%(n+1)
最大权匹配:ans/(n+1)
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int N = 2e2 + 5;
const int inf = 0x3f3f3f3f;
int a[maxn][maxn];
int b[maxn];
int n, m;
int w[N][N];
int la[N], lb[N];
int va[N], vb[N];
int match[N];
int delta;
map<string, int>mp[2];
vector<int>v[N];
int dfs(int x) {
va[x] = 1;
for (int y = 1; y <= m; y++) {
if (!vb[y]) {
if (la[x] + lb[y] - w[x][y] == 0) {
vb[y] = 1;
if (!match[y] || dfs(match[y])) {
match[y] = x;
return 1;
}
}
else delta = min(delta, la[x] + lb[y] - w[x][y]);
}
}
return 0;
}
int KM() {
for (int i = 1; i <= m; i++) {
lb[i] = 0;
}
for (int i = 1; i <= n; i++) {
la[i] = -(1 << 30);
for (int j = 1; j <= m; j++) {
la[i] = max(la[i], w[i][j]);
}
}
for (int i = 1; i <= n; i++) {
while (1) {
memset(va, 0, sizeof(va));
memset(vb, 0, sizeof(vb));
delta = 1 << 30;
if (dfs(i))break;
for (int j = 1; j <= n; j++) {
if (va[j])la[j] -= delta;
}
for (int j = 1; j <= m; j++) {
if (vb[j])lb[j] += delta;
}
}
}
int ans = 0;
for (int i = 1; i <= m; i++) {
if (match[i] == 0)continue;
ans += w[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", &a[i][j]);
}
}
int sum = 0;
memset(w, -inf, sizeof(w));
memset(match, 0, sizeof(match));
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
sum += a[i][x];
for (int j = 1; j <= m; j++) {
w[i][j] = a[i][j] * 100 + (j == x);
}
}
int ans=KM();
printf("%d %d\n",n- ans % 100, ans /100-sum);
}
}