「网络流 24 题」负载平衡

G 公司有 \(n\) 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最少搬运量可以使 \(n\) 个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。

这道题我们建立一个超级源点和超级汇点,源点向每个仓库连边,容量为仓库库存,仓库向汇点连边,容量为库存平均值。每一个点向左右连边,边权为inf。

对于这些边,我们都需要建一条与他们方向相反,容量为 \(0\) ,费用为原边的相反数。

然后直接跑一下最小费用最大流,费用即为解。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

int read() {
    int a = 0, x = 1;
    char ch = getchar();
    while (ch > '9' || ch < '0') {
        if (ch == '-')
            x = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        a = a * 10 + ch - '0';
        ch = getchar();
    }
    return a * x;
}
const int N = 807, inf = 1e9 + 7;
;
int n, arr[N], sum = 0;

int s, t, pre1[N], pre2[N];

int head[N], go[N], nxt[N], val[N], lim[N], cnt = -1;
void add(int u, int v, int r, int w) {
    go[++cnt] = v;
    nxt[cnt] = head[u];
    head[u] = cnt;
    val[cnt] = w, lim[cnt] = r;
}
int dis[N], ins[N];
bool SPFA(int pos) {
    memset(ins, 0, sizeof(ins));
    queue<int> q;
    for (int i = 1; i <= n + 1; i++) dis[i] = inf;
    dis[pos] = 0, ins[pos] = 1;
    q.push(pos);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        ins[u] = 0;
        //	printf("%d \n",u);
        for (int e = head[u]; ~e; e = nxt[e]) {
            int v = go[e];
            if (!lim[e])
                continue;
            if (dis[v] > dis[u] + val[e]) {
                dis[v] = dis[u] + val[e];
                pre1[v] = u, pre2[v] = e;
                if (!ins[v]) {
                    q.push(v);
                    ins[v] = 1;
                }
            }
        }
    }
    return dis[t] != inf;
}

void print(int i) {
    if (i == s)
        printf("%d ", s);
    else {
        print(pre1[i]);
        printf("%d ", i);
    }
}

int main() {
    n = read();
    s = 0, t = n + 1;
    memset(head, -1, sizeof(head));
    memset(nxt, -1, sizeof(nxt));
    for (int i = 1; i <= n; i++) {
        arr[i] = read();
        //  add(0, i, arr[i], 0);
        //  add(i, 0, 0, 0);
        sum += arr[i];
    }
    sum /= n;
    for (int i = 1; i <= n; i++) {
        if (arr[i] < sum) {
            add(i, n + 1, sum - arr[i], 0);
            add(n + 1, i, 0, 0);
        } else {
            add(0, i, arr[i] - sum, 0);
            add(i, 0, 0, 0);
        }
        add(i, i % n + 1, inf, 1);
        add(i % n + 1, i, 0, -1);
        add(i % n + 1, i, inf, 1);
        add(i, i % n + 1, 0, -1);
    }
    int ans = 0;
    // printf("%d\n",sum);
    while (SPFA(s)) {
        ///	printf("!");
        int tmp = inf;
        for (int i = t; i != s; i = pre1[i]) tmp = min(tmp, lim[pre2[i]]);
        ans += dis[t] * tmp;
        //     print(t);
        //    putchar('\n');
        for (int i = t; i != s; i = pre1[i]) lim[pre2[i]] -= tmp, lim[pre2[i] ^ 1] += tmp;
        // printf("%d %d\n",tmp,ans);
    }
    printf("%d", ans);
    return 0;
}
posted @ 2020-09-19 10:21  nao-nao  阅读(110)  评论(0编辑  收藏  举报