洛谷 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)

 

转载于:https://www.cnblogs.com/obob/p/9565993.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值