zoukankan      html  css  js  c++  java
  • Luogu P4016 负载平衡问题

    传送门qwq

    刚学会网络流,把网络流24题按难度sort一下,第一个蓝题就不会...(某二分图匹配除外)

    于是又跑去学了最小费用最大流。

    听说网络流的难点就在于建图,似乎感受到一点了...


    这道题和飞行员匹配一样,需要用到超级源点和汇点。

    既然要平均分配,那么首先可以先算出平均值。

    高于平均值的仓库一定有流出,低于的则有流入。

    不能把每个高于平均值的都作为源点,所以把它们都连入源点s,容量为与平均值的差值,费用为0;反之亦然。

    题中给出相邻的仓库运输费用为1,那么,将相邻的边之间连接容量为INF,费用为1的边。

    注意,由于是双向边,所以1到2要建边,2到1也要建边。

    剩下的就是最小费用最大流棵题了qwq

    代码如下

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #define MogeKo qwq
    using namespace std;
    
    const int maxn = 2e5+10;
    const int INF = 0x3f3f3f3f;
    int n,m,s,t,cnt,avr,sum;
    int a[maxn];
    int head[maxn],to[maxn],nxt[maxn],w[maxn],co[maxn];
    int fa[maxn],path[maxn],dis[maxn],fl[maxn];
    int mincost;
    bool vis[maxn];
    
    void add(int x,int y,int ww,int cc) {
        to[cnt] = y;
        nxt[cnt] = head[x];
        head[x] = cnt;
        w[cnt] = ww;
        co[cnt] = cc;
        cnt++;
    }
    
    bool SPFA() {
        memset(dis,INF,sizeof(dis));
        memset(fl,INF,sizeof(fl));
        queue <int> q;
        dis[s] = 0;
        vis[s] = true;
        q.push(s);
        while(!q.empty()) {
            int u = q.front();
            q.pop();
            vis[u] = false;
            for(int i = head[u]; i != -1; i = nxt[i]) {
                int v = to[i];
                if(dis[v] <= dis[u]+co[i] || !w[i]) continue;
                dis[v] = dis[u]+co[i];
                fa[v] = u, path[v] = i;
                fl[v] = min(fl[u],w[i]);
                if(!vis[v]) {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
        if(dis[t] == INF) return false;
        return true;
    }
    
    void mcmf() {
        while(SPFA()) {
            for(int i = t; i != s; i = fa[i]) {
                int p = path[i];
                w[p] -= fl[t];
                w[p^1] += fl[t];
            }
            mincost += fl[t] * dis[t];
        }
    }
    
    int main() {
        scanf("%d",&n);
        memset(head,-1,sizeof(head));
        for(int i = 1; i <= n; i++) {
            scanf("%d",&a[i]);
            sum += a[i];
        }
        avr = sum/n;
        s = n+1,t = n+2;
        for(int i = 1; i <= n; i++) {
            a[i] -= avr;
            if(a[i] > 0)
                add(s,i,a[i],0), add(i,s,0,0);
            if(a[i] < 0)
                add(i,t,-a[i],0), add(t,i,0,0);
        }
        add(1,n,INF,1), add(n,1,0,-1);
        add(n,1,INF,1), add(1,n,0,-1);
        for(int i = 1; i <= n; i++) {
            if(i-1 > 0) add(i,i-1,INF,1), add(i-1,i,0,-1);
            if(i+1 <= n) add(i,i+1,INF,1), add(i+1,i,0,-1);
        }
        n += 2;
        mcmf();
        printf("%d",mincost);
        return 0;
    }
    View Code
  • 相关阅读:
    Android Gradle使用总结
    Jenkins实现Android自动化打包
    RxJava 2.x 使用最佳实践
    Android 路由框架ARouter最佳实践
    Android 加载GIF图最佳实践
    Java 锁机制 synchronized
    Fiddler抓包使用教程-断点调试
    Fiddler抓包使用教程-Android应用抓包
    Fiddler抓包使用教程-QuickExec
    Fiddler抓包使用教程-模拟低速网络环境
  • 原文地址:https://www.cnblogs.com/mogeko/p/11249373.html
Copyright © 2011-2022 走看看