[网络流24题] 负载平衡问题

怎么每天都要被sb错误坑半天..建图的时候竟然忘记了反向边

这道题我们可以考虑把一个地方拆成两个点 x1,y1 x 1 , y 1 , 从源点向 x1 x 1 连流量为物品数, 费用0的边, y1 y 1 向汇点连所有物品平均数的边, 然后每个 x1 x 1 向对应 y1 y 1 连流量 inf i n f , 费用 0 0 的边, 然后对于物品间的运转, 相邻的连流量inf, 费用为 0 0 的边 一定要建双向边!!!我就是因为这里wa了很多遍, 例如1号点能向 n n 号点运输, 那n号点肯定也可以向 1 1 <script type="math/tex" id="MathJax-Element-23">1</script>号点运输, 这样子跑最小费用最大流即可
题目链接

#include<bits/stdc++.h>
#define For(i, a, b) for(register int i = a; i <= b; ++ i)
#define go(x, i) for(register int i = head[x]; i; i = nxt[i])
#define rel(x) ((x - 1) % n + 1)
#define inf (0x3f3f3f3f)

using namespace std;

const int maxn = 200 + 10, maxm = 1e5 + 10;
int n, a[maxn], sum, s, t;

namespace Mincost_Maxflow {
    int to[maxm << 1], head[maxn], nxt[maxm << 1], w[maxm << 1], v[maxm << 1], e = 1;
    int dis[maxn], vis[maxn], cost;

    void add(int x, int y, int z, int val) {
        to[++ e] = y;
        nxt[e]= head[x];
        head[x] = e;
        w[e] = z;
        v[e] = val;
        if(z) add(y, x, 0, -val);
    }

    bool SPFA() {
        queue<int> q;
        memset(vis, 0, sizeof(vis));
        memset(dis, inf, sizeof(dis));
        vis[s] = true, dis[s] = 0, q.push(s);
        while(!q.empty()) {
            int k = q.front(); q.pop();
            go(k, i) 
                if(w[i] && dis[to[i]] > dis[k] + v[i]) {
                    dis[to[i]] = dis[k] + v[i];
                    if(!vis[to[i]]) {
                        vis[to[i]] = true;
                        q.push(to[i]);
                    }
                }
            vis[k] = false;
        }
        return dis[t] != inf;
    }

    int dfs(int x, int flow) {
        vis[x] = flow;
        if(x == t || !flow)
            return flow;
        int used = 0;
        go(x, i) 
            if(dis[to[i]] == dis[x] + v[i] && !vis[to[i]]) {
                int di = dfs(to[i], min(flow, w[i]));
                w[i] -= di, w[i ^ 1] += di;
                flow -= di, used += di, cost += di * v[i];
                if(!flow) break;
            }
        return used;
    }

    int dinic() {
        int res = 0;
        while(SPFA()) {
            vis[t] = true;
            while(vis[t]) {
                memset(vis, 0, sizeof(vis));
                res += dfs(s, inf);
            }
        }
        return res;
    }

}

int main() {
#ifndef ONLINE_JUDGE
    freopen("4016.in", "r", stdin);
    freopen("4016.out", "w", stdout);
#endif
    scanf("%d", &n); s = n << 1 | 1, t = s + 1;
    For(i, 1, n) scanf("%d", &a[i]), sum += a[i];
    sum /= n;
    For(i, 1, n) Mincost_Maxflow::add(s, i, a[i], 0), Mincost_Maxflow::add(i + n, t, sum, 0);
    For(i, 1, n) {
        Mincost_Maxflow::add(i, i + n, inf, 0);
        Mincost_Maxflow::add(rel(i), rel(i + 1), inf, 1);
        Mincost_Maxflow::add(rel(i + 1), rel(i), inf, 1);
    }
    Mincost_Maxflow::dinic(), printf("%d\n", Mincost_Maxflow::cost);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值