Libre 6013 「网络流 24 题」负载平衡 (网络流,最小费用最大流)

Libre 6013 「网络流 24 题」负载平衡 (网络流,最小费用最大流)

Description

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

Input

第1 行中有1 个正整数n(n<=100),表示有n个仓库。
第2 行中有n个正整数,表示n个仓库的库存量。

Output

将计算出的最少搬运量输出

Sample Input

5
17 9 14 16 4

Sample Output

11

Http

Libre:https://loj.ac/problem/6013

Source

网络流,最小费用最大流

解决思路

对于每一个仓库,我们从源点连出一条容量为仓库存储的货物数量费用为0的边,连到汇点连一条容量库存数目总和/n花费为0的边。在对于每一个相邻的仓库连容量为无穷大费用为1的边。这样跑一边最小费用最大流就可以得到最终的答案

代码

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

const int maxN=250;
const int maxM=maxN*maxN*4;
const int inf=2147483647;

class Edge
{
public:
    int u,v,cost,flow;
};

int n;
int cnt=-1;
int Head[maxN];
int Next[maxM];
Edge E[maxM];
int Dist[maxN];
int Flow[maxN];
int Path[maxN];
bool inqueue[maxN];
int Q[maxM];

void Add_Edge(int u,int v,int cost,int flow);
bool spfa();

int main()
{
    memset(Head,-1,sizeof(Head));
    int sum=0;
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        int num;
        scanf("%d",&num);
        sum+=num;//求和,因为要统计所有仓库库存之和
        Add_Edge(0,i,0,num);//连接源点与仓库i
        if (i==1)//连接i与其前一个仓库,注意判断1的情况
            Add_Edge(1,n,1,inf);
        else
            Add_Edge(i,i-1,1,inf);
        if (i==n)//连接i与后一个仓库,注意判断n的情况
            Add_Edge(n,1,1,inf);
        else
            Add_Edge(i,i+1,1,inf);
    }
    sum=sum/n;
    for (int i=1;i<=n;i++)//连接i与汇点
        Add_Edge(i,n+1,0,sum);
    int Ans=0;//最小费用最大流
    while (spfa())
    {
        int now=n+1;
        int last=Path[now];
        while (now!=0)
        {
            E[last].flow-=Flow[n+1];
            E[last^1].flow+=Flow[n+1];
            now=E[last].u;
            last=Path[now];
        }
        Ans+=Dist[n+1]*Flow[n+1];
    }
    cout<<Ans<<endl;
    return 0;
}

void Add_Edge(int u,int v,int cost,int flow)
{
    cnt++;
    Next[cnt]=Head[u];
    Head[u]=cnt;
    E[cnt].u=u;
    E[cnt].v=v;
    E[cnt].flow=flow;
    E[cnt].cost=cost;

    cnt++;
    Next[cnt]=Head[v];
    Head[v]=cnt;
    E[cnt].u=v;
    E[cnt].v=u;
    E[cnt].flow=0;
    E[cnt].cost=-cost;
}

bool spfa()
{
    for (int i=0;i<=n+1;i++)
        Dist[i]=inf;
    memset(inqueue,0,sizeof(inqueue));
    Dist[0]=0;
    inqueue[0]=1;
    int h=1,t=0;
    Q[1]=0;
    Flow[0]=inf;
    do
    {
        t++;
        int u=Q[t];
        inqueue[u]=0;
        for (int i=Head[u];i!=-1;i=Next[i])
        {
            int v=E[i].v;
            if ((E[i].flow>0)&&(Dist[u]+E[i].cost<Dist[v]))
            {
                Dist[v]=Dist[u]+E[i].cost;
                Flow[v]=min(Flow[u],E[i].flow);
                Path[v]=i;
                if (inqueue[v]==0)
                {
                    h++;
                    Q[h]=v;
                    inqueue[v]=1;
                }
            }
        }
    }
    while(h!=t);
    if (Dist[n+1]==inf)
        return 0;
    return 1;
}

转载于:https://www.cnblogs.com/SYCstudio/p/7309169.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值