zoukankan      html  css  js  c++  java
  • 【LOJ】 #6012. 「网络流 24 题」分配问题

    题解

    又写了一遍KM算法,这题刚好是把最大最小KM拼在一起写的,感觉比较有记录价值

    感觉KM始终不熟啊QAQ

    算法流程大抵如下,原理就是每次我们通过减少最少的匹配量达成最大匹配,所以获得的一定是最大价值

    1.我们先给左部点求一个期望大小,如果是最大KM,期望大小就是最大的那条边的权值,如果是最小KM,期望大小就是最小的那条边的权值
    2.然后跑二分图匹配,两个点能匹配的条件是左点(u)的期望值加右点(v)的期望值刚好是边权
    3.给无法访问的点更新断层大小,如果是最小匹配,那么断层就是(c[u][v] - (ex_l[u] + ex_r[v])),如果是最大匹配,就是((ex_l[u] + ex_r[v]) - c[u][v])
    4.在没有被访问的右点里寻找最小的减少量(d)
    5.给访问过的左点和右点,如果是最小匹配,左点加上(d),因为要包括进一些更大的边,右点减去(d),如果是最大匹配,左点减去(d),因为要包括进一些更小的边,右点加上(d)

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int, int>
    #define pdi pair<db, int>
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define eps 1e-8
    #define mo 974711
    #define MAXN 205
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template <class T>
    void read(T &res) {
        res = 0;
        char c = getchar();
        T f = 1;
        while (c < '0' || c > '9') {
            if (c == '-')
                f = -1;
            c = getchar();
        }
        while (c >= '0' && c <= '9') {
            res = res * 10 + c - '0';
            c = getchar();
        }
        res *= f;
    }
    template <class T>
    void out(T x) {
        if (x < 0) {
            x = -x;
            putchar('-');
        }
        if (x >= 10) {
            out(x / 10);
        }
        putchar('0' + x % 10);
    }
    int N;
    int c[105][105];
    int ex_l[105], ex_r[105], slack[105], matc[105];
    bool vis_l[105], vis_r[105];
    bool match(int u, int on) {
        vis_l[u] = 1;
        for (int v = 1; v <= N; ++v) {
            if (!vis_r[v]) {
                if (vis_r[v])
                    continue;
                int gap;
                if (on == 0)
                    gap = c[u][v] - ex_l[u] - ex_r[v];
                else
                    gap = ex_l[u] + ex_r[v] - c[u][v];
                if (gap == 0) {
                    vis_r[v] = 1;
                    if (!matc[v] || match(matc[v], on)) {
                        matc[v] = u;
                        return true;
                    }
                } else
                    slack[v] = min(slack[v], gap);
            }
        }
        return false;
    }
    int KM(int on) {
        for (int i = 1; i <= N; ++i) {
            ex_r[i] = 0;
            if (on == 0)
                ex_l[i] = 0x7fffffff;
            else
                ex_l[i] = 0;
            for (int j = 1; j <= N; ++j) {
                if (on == 0)
                    ex_l[i] = min(ex_l[i], c[i][j]);
                else
                    ex_l[i] = max(ex_l[i], c[i][j]);
            }
        }
        memset(matc, 0, sizeof(matc));
        for (int i = 1; i <= N; ++i) {
            for (int j = 1; j <= N; ++j) slack[j] = 0x7fffffff;
            while (1) {
                memset(vis_l, 0, sizeof(vis_l));
                memset(vis_r, 0, sizeof(vis_r));
                if (match(i, on))
                    break;
                int d = 0x7fffffff;
                for (int j = 1; j <= N; ++j) {
                    if (!vis_r[j])
                        d = min(d, slack[j]);
                }
                for (int j = 1; j <= N; ++j) {
                    if (on == 0) {
                        if (vis_l[j])
                            ex_l[j] += d;
                        if (vis_r[j])
                            ex_r[j] -= d;
                        else
                            slack[j] -= d;
                    } else {
                        if (vis_l[j])
                            ex_l[j] -= d;
                        if (vis_r[j])
                            ex_r[j] += d;
                        else
                            slack[j] -= d;
                    }
                }
            }
        }
        int res = 0;
        for (int v = 1; v <= N; ++v) {
            res += c[matc[v]][v];
        }
        return res;
    }
    void Solve() {
        read(N);
        for (int i = 1; i <= N; ++i) {
            for (int j = 1; j <= N; ++j) {
                read(c[i][j]);
            }
        }
        out(KM(0));
        enter;
        out(KM(1));
        enter;
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in", "r", stdin);
    #endif
        Solve();
        return 0;
    }
    
  • 相关阅读:
    【IT学习资源】2013.10.30
    【转载】 Bash之read命令
    【书本目录】 -- ABS(advanced bash scripts)
    【转载】vSphere的使用
    【转载】 Linux命令 -- tr 转换字符
    【转载】Oracle的日常监控脚本
    【转载】Nginx基础:6.webcache缓存服务
    大学记忆(3)[三国杀(终)]
    大学记忆(1)[记忆之殇]
    大学记忆(2)[计算机]
  • 原文地址:https://www.cnblogs.com/ivorysi/p/10097771.html
Copyright © 2011-2022 走看看