「网络流 24 题」负载平衡 【费用流】

「网络流 24 题」负载平衡

1

思路

首先我们从源点向每个仓库连边,容量为 a i a_i ai,费用为 0 0 0;既然所有仓库物品相同,那么数量一定是总物品的平均值,我们提前算出来 a v g avg avg,然后从每个仓库向汇点连边,容量为 a v g avg avg,费用为 0 0 0,最后在相邻仓库之间连上容量 ∞ \infty ,费用为 1 1 1 的边,跑最小费用最大流即可

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;

const int INF=0x3f3f3f3f;
const long long INFLL=1e18;

typedef long long ll;

struct MCF {
    struct Edge {
        int v, c, w; //边终点、容量、费用
        Edge(int v, int c, int w) : v(v), c(c), w(w) {}
    };
    const int n;
    std::vector<Edge> e;
    std::vector<std::vector<int>> g;
    std::vector<ll> h, dis;
    std::vector<int> pre;
    bool dijkstra(int s, int t) {
        dis.assign(n + 1, std::numeric_limits<ll>::max());
        pre.assign(n + 1, -1);
        std::priority_queue<std::pair<ll, int>, std::vector<std::pair<ll, int>>, std::greater<std::pair<ll, int>>> que;
        dis[s] = 0;
        que.emplace(0, s);
        while (!que.empty()) {
            ll d = que.top().first;
            int u = que.top().second;
            que.pop();
            if (dis[u] < d) continue;
            for (int i : g[u]) {
                int v = e[i].v;
                int c = e[i].c;
                int w = e[i].w;
                if (c > 0 && dis[v] > d + h[u] - h[v] + w) {
                    dis[v] = d + h[u] - h[v] + w;
                    pre[v] = i;
                    que.emplace(dis[v], v);
                }
            }
        }
        return dis[t] != std::numeric_limits<ll>::max();
    }
    MCF(int n) : n(n), g(n + 1) {}
    void addEdge(int u, int v, int c, int w) {
        g[u].push_back(e.size());
        e.emplace_back(v, c, w);
        g[v].push_back(e.size());
        e.emplace_back(u, 0, -w);
    }
    std::pair<int, ll> flow(int s, int t) {
        int flow = 0;
        ll cost = 0;
        h.assign(n + 1, 0);
        while (dijkstra(s, t)) {
            for (int i = 1; i <= n; ++i) h[i] += dis[i];
            int aug = std::numeric_limits<int>::max();
            for (int i = t; i != s; i = e[pre[i] ^ 1].v) aug = std::min(aug, e[pre[i]].c);
            for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
                e[pre[i]].c -= aug;
                e[pre[i] ^ 1].c += aug;
            }
            flow += aug;
            cost += ll(aug) * h[t];
        }
        return std::make_pair(flow, cost);
    }
};

int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    int n;
    std::cin >> n;
    MCF mcf(n + 2);
    int S = n + 1, T =  S + 1;
    std::vector<int> a(n + 1, 0);
    fore(i, 1, n + 1){
        std::cin >> a[i];
        mcf.addEdge(S, i, a[i], 0);
    }
    int avg = std::accumulate(ALL(a), 0) / n;
    fore(i, 1, n + 1) mcf.addEdge(i, T, avg, 0);
    fore(i, 2, n + 1){
        mcf.addEdge(i - 1, i, INF, 1);
        mcf.addEdge(i, i - 1, INF, 1);
    }
    mcf.addEdge(1, n, INF, 1);
    mcf.addEdge(n, 1, INF, 1);

    std::cout << mcf.flow(S, T).se;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值