zoukankan      html  css  js  c++  java
  • 【图论】KM算法

    https://github.com/lydrainbowcat/tedukuri/blob/master/配套光盘/正文包含的程序片段/0x68 bipartite_graph_match.cpp

    // 非递归版,O(n^3),UOJ80 https://uoj.ac/problem/80
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<cmath>
    using namespace std;
    const int N = 405;
    int w[N][N]; // 边权
    long long la[N], lb[N], upd[N]; // 左、右部点的顶标
    bool va[N], vb[N]; // 访问标记:是否在交错树中
    int match[N], last[N]; // 右部点匹配了哪一个左部点
    int n, nl, nr, m, boy[N];
    
    void search(int x) {
        memset(va, 0, sizeof(va));
        memset(vb, 0, sizeof(vb));
        memset(last, 0, sizeof(last));
        for (int j = 1; j <= n; j++) upd[j] = 1ll << 60;
        int y = 0;
        match[0] = x;
        do {
            va[x] = true;
            long long delta = 1ll << 60; // inf
            int next_y = 0;
            for (int j = 1; j <= n; j++)
                if (!vb[j]) {
                    if (upd[j] > la[x] + lb[j] - w[x][j]) {
                        upd[j] = la[x] + lb[j] - w[x][j];
                        last[j] = y;
                    }
                    if (upd[j] < delta) {
                        delta = upd[j];
                        next_y = j;
                    }
                }
            // 当delta=0时,相当于沿着相等子图向下搜索一层
            // 当delta>0时,相当于直接回到最小边(新加入相等子图的边)处开始搜索
            if (delta) {
                for (int j = 1; j <= n; j++) { // 修改顶标
                    if (va[j]) la[j] -= delta;
                    if (vb[j]) lb[j] += delta;
                    else upd[j] -= delta;
                }
            }
            vb[y = next_y] = true;
        } while (x = match[y]); // 直到找到增广路
        while (y) {
            match[y] = match[last[y]];
            y = last[y];
        }
    }
    
    long long KM() {
        for (int i = 1; i <= n; i++) {
            la[i] = -(1 << 30); // -inf
            lb[i] = 0;
            for (int j = 1; j <= n; j++)
                la[i] = max(la[i], 1ll * w[i][j]);
        }
        for (int i = 1; i <= n; i++) search(i);
        long long ans = 0;
        for (int i = 1; i <= n; i++)
            ans += w[match[i]][i];
        return ans;
    }
    
    int main() {
        cin >> nl >> nr >> m;
        n = max(nl, nr);
        for (int i = 1; i <= m; i++) {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            w[x][y] = z;
        }
        cout << KM() << endl;
        for (int i = 1; i <= nr; i++)
            if (match[i] && w[match[i]][i]) boy[match[i]] = i;
        for (int i = 1; i <= nl; i++) printf("%d ", boy[i]);
        puts("");
    }
    
    
  • 相关阅读:
    [APIO2007]风铃
    [APIO2011]方格染色
    [APIO2016]烟火表演
    [APIO2013]机器人
    [APIO2015]巴厘岛的雕塑
    [APIO2015]八邻旁之桥
    [APIO2010]特别行动队
    [APIO2015]雅加达的摩天楼
    WC2019游记
    退役前的做题记录3.0
  • 原文地址:https://www.cnblogs.com/purinliang/p/14419277.html
Copyright © 2011-2022 走看看