zoukankan      html  css  js  c++  java
  • P4016 负载平衡问题(最小费用最大流)

    P4016 负载平衡问题

    题目描述

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

    输入格式

    文件的第 11 行中有 11 个正整数 nn,表示有 nn 个仓库。

    第 22 行中有 nn 个正整数,表示 nn 个仓库的库存量。

    输出格式

    输出最少搬运量。

    输入输出样例

    输入 #1复制

    5
    17 9 14 16 4
    

    输出 #1复制

    11
    

    说明/提示

    (1<=n<=100)

    思路:

    「问题分析」

    转化为供求平衡问题,用最小费用最大流解决。

    「建模方法」

    首先求出所有仓库存货量平均值,设第i个仓库的盈余量为A[i],A[i] = 第i个仓库原有存货量 – 平均存货量。建立二分图,把每个仓库抽象为两个节点Xi和Yi。增设附加源S汇T。

    1、如果A[i]>0,从S向Xi连一条容量为A[i],费用为0的有向边。
    2、如果A[i]<0,从Yi向T连一条容量为-A[i],费用为0的有向边。
    3、每个Xi向两个相邻顶点j,从Xi到Xj连接一条容量为无穷大,费用为1的有向边,从Xi到Yj连接一条容量为无穷大,费用为1的有向边。

    求最小费用最大流,最小费用流值就是最少搬运量。

    「建模分析」

    计算出每个仓库的盈余后,可以把问题转化为供求问题。建立供求网络,把二分图X集合中所有节点看做供应节点,Y集合所有节点看做需求节点,在能一次搬运满足供需的Xi和Yj之间连接一条费用为1的有向边,表示搬运一个单位货物费用为1。另外还要在Xi与相邻的Xj之间连接边,表示货物可以暂时搬运过去,不立即满足需求,费用也为1。最大流满足了所有的盈余和亏损供求平衡,最小费用就是最少搬运量。

    以上拷贝自:http://hzwer.com/1955.html 这位聚聚博主。

    我的代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <map>
    #include <set>
    #include <vector>
    #include <iomanip>
    #define ALL(x) (x).begin(), (x).end()
    #define sz(a) int(a.size())
    #define rep(i,x,n) for(int i=x;i<n;i++)
    #define repd(i,x,n) for(int i=x;i<=n;i++)
    #define pii pair<int,int>
    #define pll pair<long long ,long long>
    #define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
    #define MS0(X) memset((X), 0, sizeof((X)))
    #define MSC0(X) memset((X), '', sizeof((X)))
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define eps 1e-6
    #define gg(x) getInt(&x)
    #define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
    #define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
    #define du2(a,b) scanf("%d %d",&(a),&(b))
    #define du1(a) scanf("%d",&(a));
    using namespace std;
    typedef long long ll;
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
    ll powmod(ll a, ll b, ll MOD) {a %= MOD; if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
    void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("
    ");}}}
    void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("
    ");}}}
    
    inline void getInt(int* p);
    const int maxn = 1000010;
    // const int inf = 0x3f3f3f3f;
    /*** TEMPLATE CODE * * STARTS HERE ***/
    const int MAX_N = 1000;
    const int MAX_M = 10000;
    const int inf = 0x3f3f3f3f;
    // int s, t;
    struct edge {
        int v, c, w, next;  // v 表示边的另一个顶点,c 表示当前剩余容量,w 表示单位流量费用
    } e[MAX_M];
    int p[MAX_N], s, t, eid;  // s 表示源点,t 表示汇点,需要在进行 costflow 之前设置完毕
    void init() {
        memset(p, -1, sizeof(p));
        eid = 0;
    }
    void insert(int u, int v, int c, int w) {
        e[eid].v = v;
        e[eid].c = c;
        e[eid].w = w;
        e[eid].next = p[u];
        p[u] = eid++;
    }
    void addedge(int u, int v, int c, int w) {
        insert(u, v, c, w);
        insert(v, u, 0, -w);
    }
    bool inq[MAX_N];
    int d[MAX_N];  // 如果到顶点 i 的距离是 0x3f3f3f3f,则说明不存在源点到 i 的最短路
    int pre[MAX_N];  // 最短路中连向当前顶点的边的编号
    bool spfa() {  // 以源点 s 为起点计算单源最短路,如果不存在从 s 到 t 的路径则返回 false,否则返回 true
        memset(inq, 0, sizeof(inq));
        memset(d, 0x3f, sizeof(d));
        memset(pre, -1, sizeof(pre));
        d[s] = 0;
        inq[s] = true;
        queue<int> q;
        q.push(s);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            inq[u] = false;
            for (int i = p[u]; i != -1; i = e[i].next) {
                if (e[i].c) {    //注意这个条件!!!spfa这里是以w求最短路的,但仍然不能忽略容量c的考虑!
                    int v = e[i].v;
                    if (d[u] + e[i].w < d[v]) {
                        d[v] = d[u] + e[i].w;
                        pre[v] = i;
                        if (!inq[v]) {
                            q.push(v);
                            inq[v] = true;
                        }
                    }
                }
            }
        }
        return pre[t] != -1;
    }
    
    int min_cost_flow() {  // 计算最小费用最大流
        int ret = 0;  // 累加和
        while (spfa()) {
            int flow = inf;
            for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
                flow = min(e[pre[i]].c, flow);  // 计算当前增广路上的最小流量
            }
            for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
                e[pre[i]].c -= flow;     //容量是一定要跟着变化的,毕竟要继续循环使用spfa来更新下一条“能走的(看容量)”最短路。
                e[pre[i] ^ 1].c += flow;
                ret += e[pre[i]].w * flow;
            }
        }
        return ret;
    }
    
    int n;
    int a[MAX_N];
    int main()
    {
        //freopen("D:\code\text\input.txt","r",stdin);
        //freopen("D:\code\text\output.txt","w",stdout);
        init();
        du1(n);
        int sum = 0;
        repd(i, 1, n)
        {
            du1(a[i]);
            sum += a[i];
        }
        sum /= n;
        s = 0;
        t = n * 2 + 1;
        repd(i, 1, n)
        {
            a[i] -= sum;
            if (a[i] > 0)
            {
                addedge(s, i, a[i], 0);
            } else
            {
                addedge(i + n, t, -a[i], 0);
            }
            if (i - 1 > 0)
            {
                addedge(i, i + n - 1, inf, 1);
                addedge(i, i - 1, inf, 1);
            }
            if (i + 1 < n + 1)
            {
                addedge(i, i + n + 1, inf, 1);
                addedge(i, i + 1, inf, 1);
            }
        }
        addedge(1, n, inf, 1);
        addedge(1, n * 2, inf, 1);
        addedge(n, 1, inf, 1);
        addedge(n, 1 + n, inf, 1);
        printf("%d
    ", min_cost_flow() );
        return 0;
    }
    
    inline void getInt(int* p) {
        char ch;
        do {
            ch = getchar();
        } while (ch == ' ' || ch == '
    ');
        if (ch == '-') {
            *p = -(getchar() - '0');
            while ((ch = getchar()) >= '0' && ch <= '9') {
                *p = *p * 10 - ch + '0';
            }
        }
        else {
            *p = ch - '0';
            while ((ch = getchar()) >= '0' && ch <= '9') {
                *p = *p * 10 + ch - '0';
            }
        }
    }
    
    
    
    
    
    本博客为本人原创,如需转载,请必须声明博客的源地址。 本人博客地址为:www.cnblogs.com/qieqiemin/ 希望所写的文章对您有帮助。
  • 相关阅读:
    subprocess(子进程模块)
    logging日志模块,hashlib hash算法相关的库,
    json pickle xml shelve configparser
    os与操作系统进行交互,sys解释器相关,random随机数,shutil解压和压缩
    目录规范+时间模块
    vue的组件
    drf 分页
    包和模块
    docker镜像&nginx配置
    匿名函数 递归 二分法 面向过程编程
  • 原文地址:https://www.cnblogs.com/qieqiemin/p/11629190.html
Copyright © 2011-2022 走看看