uva11383 少林决胜 --- KM算法

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/lmhlmh_/article/details/102748850

题解:比较经典的做法,题目给的不等式row[i] + col[j] >= w[i][j]和二分图最佳完美匹配的很像,可以联想到KM算法, 把横纵坐标分别看作二分图的顶点,每个w(i,j)看作边权值,跑一遍匈牙利算法即可。

(KM算法的过程实际是最大化边权和,最小化顶标和的过程

证明是最小化订标只需在最佳完美匹配的状态时,假设减少某点的订标引入新边,那么原来与此点连接的另外一点的订标也要增加那么多,那订标和就减不少

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 510;
    int n;
    int w[maxn][maxn];
    int lx[maxn],ly[maxn],S[maxn],T[maxn];
    int lefts[maxn],rights[maxn];

    bool dfs(int u) {
        S[u] = true;
        for(int i = 1;i <= n;i++) if(lx[u]+ly[i]==w[u][i]&&!T[i]){
            T[i] = true;
            if(lefts[i]==0||dfs(lefts[i])) {
                lefts[i] = u;
                rights[u] = i;
                return true;
            }
        }
        return false;
    }

    void up() {
        int a = 1e9;
        for(int i = 1;i <= n;i++) if(S[i])
        for(int j = 1;j <= n;j++) if(!T[j]) {
            a = min(a,lx[i]+ly[j]-w[i][j]);
        }
        for(int i = 1;i <= n;i++) {
            if(S[i]) lx[i] -= a;
            if(T[i]) ly[i] += a;    
        }
    }

    void KM() {
        memset(lefts,0,sizeof(lefts));
        memset(rights,0,sizeof(rights));
        for(int i = 1;i <= n;i++) {
            lx[i] = ly[i] = 0;
            for(int j = 1;j <= n;j++) {
                lx[i] = max(lx[i],w[i][j]);
            }
        }
        for(int i = 1;i <= n;i++) {
            while(true) {
                memset(S,0,sizeof(S));
                memset(T,0,sizeof(T));
                if(dfs(i)) break; else up();
            }
        }
    }


    int main() {
        while(scanf("%d",&n) == 1) {
            for(int i = 1;i <= n;i++)
                for(int j = 1;j <= n;j++) scanf("%d",&w[i][j]);
            KM();
            int t = 0;
            for(int i = 1;i <= n;i++) printf("%d ",lx[i]),t+=lx[i];
            printf("\n");
            for(int i = 1;i <= n;i++) printf("%d ",ly[i]),t+=ly[i];
            printf("\n%d\n",t);
            
        }

        return 0;
    }

猜你喜欢

转载自blog.csdn.net/lmhlmh_/article/details/102748850