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

    题目描述

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

     问题分析

    目标状态为所有仓库货物数量相等,每个仓库目前的货物数量减平衡数量  a[i]-s > 0 时,此仓库将向外运输货物,否则接收货物,所以我们的目标即为将所有货物从供应货物处转移到需求货物处,并使得转移花费最小.

    所以从源点向每一个供应货物的点建边,容量为当前点可供应货物数;从每一个需求货物点向汇点建边,容量为需求货物数.每两个相邻的点之间都彼此有边,容量INF,单位花费为1.在当前网络上跑最小费用最大流即为答案.

    代码

    //最小费用最大流 复杂度O(N*M*F)
    //有向图
    #include <bits/stdc++.h>
    using namespace std;
    
    #define N 105
    #define M 20005
    int s,t;
    int dis[N],vis[N],pre[N],head[N];
    int cnt;
    struct Edge{
        int to,next,w,c;//w容量,c花费
    }edge[M<<1];
    int INF = 0x3f3f3f3f;
    
    void ad(int u,int v,int w,int c){
        edge[cnt].to = v,edge[cnt].next = head[u],edge[cnt].w = w,edge[cnt].c = c,head[u] = cnt++;
        edge[cnt].to = u,edge[cnt].next = head[v],edge[cnt].w = 0,edge[cnt].c = -c,head[v] = cnt++;
    }
    int spfa(){
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        //memset(pre,-1,sizeof(pre));
        pre[s] = pre[t] = -1;
        queue<int> Q;
        int u,v;
        while(!Q.empty()) Q.pop();
        Q.push(s);
        vis[s] = 1;dis[s] = 0;
        while(!Q.empty()){
            u = Q.front();
            vis[u] = 0;
            Q.pop();
            for(int i = head[u];i != -1;i = edge[i].next){
                v = edge[i].to;
                if(!edge[i].w) continue;
                if(dis[v] > dis[u] + edge[i].c){
                    dis[v] = dis[u] + edge[i].c;
                    pre[v] = i^1;
                    if(!vis[v]) Q.push(v);
                    vis[v] = 1;
                }
            }
        }
        if(pre[t] == -1) return 0;
        return dis[t];
    }
    
    int flow(){
        int f = INF;
        for(int i = pre[t];i != -1;i = pre[edge[i].to]){
            f = min(f,edge[i^1].w);
        }
        for(int i = pre[t];i != -1;i = pre[edge[i].to]){
            edge[i].w += f;
            edge[i^1].w -= f;
        }
        return f;
    }
    
    
    int EK(){
        int cost,sum = 0;
        while((cost = spfa())) sum += cost * flow();
        return sum;
    }
    int num[N];
    void getMap(){
        int n;
        int sum = 0;
        scanf("%d",&n);
        for(int i = 1;i <= n;++i){
            scanf("%d",&num[i]);
            sum += num[i];
        }
        for(int i = 1;i <= n;++i){
            num[i] = num[i] - sum/n;
            if(num[i] > 0) ad(0,i,num[i],0);
            else ad(i,n+1,-num[i],0);
        }
        ad(n,1,INF,1);ad(1,n,INF,1);
        for(int i = 1;i < n;++i){
            ad(i,i+1,INF,1);
            ad(i+1,i,INF,1);
        }
        s = 0,t = n+1;
        //建图
        //初始化s,t
    }
    
    
    int main()
    {
        memset(head,-1,sizeof(head));
        cnt = 0;
        getMap();
        printf("%d\n",EK());
        return 0;
    }

    强无敌的另解贪心:

     

    先考虑一个更简单的负载平衡问题:

      n个人站成一行,每个人手中有a[i]张纸牌,每次传递纸牌必须是一个人将一张纸牌交给与之相邻的另一个人,问最少传递多少次能使所有人手中的纸牌数相同.

    问题分析:

      首先传递纸牌不能交叉传递,所以每一个人要么从外界接收纸牌,要么向外界输送。 

      平衡状态时,每个人手中都有s张纸牌。纸牌一定是从一个多余纸牌的区域传递向缺少纸牌的区域。假如前i个人共有sum[i]张纸牌,他们在平衡状态时应有s*i张纸牌,那么他们缺少(多余)的纸牌一定来源于第i+1个人,第i+1个人会向第i个人运送| s*i-sum[i] |张纸牌。至于第i+1个人的纸牌,是从更后面传递过来的。所以最终传递纸牌的花费即为∑|sum[i]-s*i|。复杂度O(n)

      

    原问题:

      站成一圈时,我们可以枚举两个彼此不交换卡牌的人,然后把环扯成一条链,问题又转化为简化问题了。

      复杂度O(n*n)

  • 相关阅读:
    苹果全球营销高级副总裁Phil Schiller曾考虑炒掉长期创意代理商Media Arts Lab
    Amazon Seller Central is Temporarily Unavailable
    三星高管:我们手机卖的好是因为营销成功
    欧洲跨境电商增速将达21% 德国力压群雄
    苹果挖走了亚马逊搜索技术副总裁,或为提升应用商店搜索功能
    亚马逊CEO贝索斯致股东信:阐述公司未来计划
    Amazon.com 购物 信用卡预售期
    佛论婆媳关系
    Focalprice李培亮:梦想让人在我店里排队
    DX孟虎点评新兴市场:巴西俄罗斯火爆背后
  • 原文地址:https://www.cnblogs.com/obob/p/9565993.html
Copyright © 2011-2022 走看看