UVA 11183 - Golden Tiger Claw(二分图最佳匹配)

题目链接 https://cn.vjudge.net/problem/UVA-11383

【题意】
给一个nn的矩阵,每个格子中有正整数w[i][j],试为每行和每列分别确定一个数字row[i]col[i],使得任意格子w[i][j]<=row[i]+col[j]恒成立。先输row,再输出col,再输出全部总和(总和应尽量小)(n<=500,w[i][j]<=100)

【思路】
  本题与匹配无关,但可以用KM算法解决。
  KM算法中的顶标就是保持了Lx[i]+ly[j]>=g[i][j]再求最大权和匹配的,但这个最大权和并没有关系。我们可以将row[i]看成一个男的,col[i]看成一个女的,这样男女的总数就相等。

  一般来说,Lx[i]或Ly[i]仅需要取该行/列中最大的那个数即可保证满足要求,但是这样太大了,可以通过调整来使得总和更小。而KM算法的过程就是一个调整的过程,每一对匹配的男女的那条边的权值就会满足等号 w[i][j]=row[i]+col[j],至少需要一个来满足等号,这样才能保证row[i]+col[j]是达到最小的,即从j列看,col[j]满足条件且最小,从i行看,row[i]满足条件且最小。这刚好与KM算法求最大权和一样。

#include<bits/stdc++.h>
using namespace std;

typedef int type;

const type inf = 2e9;
const double eps = 1e-8;
const int maxn = 505;

int n, m;
int matchx[maxn], matchy[maxn];
int visx[maxn], visy[maxn];
type lx[maxn], ly[maxn];
type w[maxn][maxn];
type slack[maxn];

bool dfs(int x) {
    visx[x] = 1;
    for (int y = 0; y < m; ++y) {
        if (visy[y]) continue;
        type tmp = lx[x] + ly[y] - w[x][y];
        if (tmp == 0) {
            visy[y] = 1;
            if (matchy[y] == -1 || dfs(matchy[y])) {
                matchx[x] = y;
                matchy[y] = x;
                return true;
            }
        }
        else {
            slack[y] = min(slack[y], tmp);
        }
    }
    return false;
}

void KM() {
    memset(matchy, -1, sizeof(matchy));
    memset(ly, 0, sizeof(ly));
    for (int i = 0; i < n; ++i) {
        lx[i] = -inf;
        for (int j = 0; j < m; ++j) {
            lx[i] = max(lx[i], w[i][j]);
        }
    }
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) slack[j] = inf;
        while (1) {
            memset(visx, 0, sizeof(visx));
            memset(visy, 0, sizeof(visy));
            if (dfs(i)) break;
            type d = inf;
            for (int j = 0; j < m; ++j) {
                if (!visy[j]) d = min(d, slack[j]);
            }
            for (int j = 0; j < n; ++j) { if (visx[j]) lx[j] -= d; }
            for (int j = 0; j < m; ++j) {
                if (visy[j]) ly[j] += d;
                else slack[j] -= d;
            }
        }
    }
}

int main() {
    while (scanf("%d", &n) == 1) {
        m = n;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                scanf("%d", &w[i][j]);
            }
        }
        KM();
        int ans = 0;
        for(int i=0;i<n;++i){
            printf("%d%c",lx[i],i+1==n?'\n':' ');
            ans+=lx[i];
        }
        for(int i=0;i<m;++i){
            printf("%d%c",ly[i],i+1==m?'\n':' ');
            ans+=ly[i];
        }
        printf("%d\n", ans);
    }
    return 0;
}
posted @ 2018-09-13 16:11  不想吃WA的咸鱼  阅读(98)  评论(0编辑  收藏  举报