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;
}
posted @ 2020-08-22 22:02  shuitiangong  阅读(111)  评论(0编辑  收藏  举报