[网络流24题]负载平衡问题——最小费用最大流

题目大意:

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

思路:

最小费用最大流,多了的仓库要把东西搬出去,少的要搬进来,限制流量之后按照路程设置费用,中间的边的流量可以设置为inf。

/*=======================
Author : ylsoi
Problem : luogu4016
Algorithm : MCMF
Time : 2018.7.26
=======================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("luogu4016.in","r",stdin);
    freopen("luogu4016.out","w",stdout);
}

const int maxn=100+10;
const int maxm=2e4+10;
const int inf=0x3f3f3f3f;
int ss,tt,n,a[maxn],sum,ans;
int las[maxm<<1],to[maxm<<1],beg[maxm],flow[maxm<<1],cost[maxm<<1],cnte=1;

void add(int u,int v,int f,int c){
    las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; flow[cnte]=f; cost[cnte]=c;
    las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u; flow[cnte]=0; cost[cnte]=-c;
}

struct mcmf{
    int dis[maxn],cur[maxn];
    bool vis[maxn];
    queue<int>qu;
    bool spfa(){
        memset(dis,63,sizeof(dis));
        dis[ss]=0;
        qu.push(ss);
        while(qu.size()){
            int u=qu.front();
            qu.pop(); vis[u]=0;
            for(int i=beg[u];i;i=las[i]){
                if(!flow[i])continue;
                if(dis[u]+cost[i]<dis[to[i]]){
                    dis[to[i]]=dis[u]+cost[i];
                    if(!vis[to[i]]){
                        vis[to[i]]=1;
                        qu.push(to[i]);
                    }
                }
            }
        }
        return dis[tt]!=inf;
    }
    int dfs(int u,int gap){
        if(u==tt || !gap)return gap;
        vis[u]=1;
        int ret=0,f;
        for(int &i=cur[u];i;i=las[i]){
            if(vis[to[i]] || dis[u]+cost[i]!=dis[to[i]])continue;
            if((f=dfs(to[i],min(flow[i],gap)))){
                gap-=f;
                ret+=f;
                flow[i]-=f;
                flow[i^1]+=f;
            }
            if(!gap)break;
        }
        vis[u]=0;
        return ret;
    }
    void work(){
        while(spfa()){
            REP(i,ss,tt)cur[i]=beg[i];
            ans+=dis[tt]*dfs(ss,inf);
        }
    }
}T;

void init(){
    scanf("%d",&n);
    REP(i,1,n){
        scanf("%d",&a[i]);
        sum+=a[i];
    }
    REP(i,1,n)a[i]-=sum/n;
    ss=0; tt=n+1;
    REP(i,1,n)if(a[i]>0)
        add(ss,i,a[i],0);
    else add(i,tt,-a[i],0);
    REP(i,1,n)if(a[i]>0)
        REP(j,1,n)if(a[j]<0)
            add(i,j,inf,min(abs(i-j),n-abs(i-j)));
}

int main(){
    File();
    init();
    T.work();
    printf("%d\n",ans);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值