网络流24题——负载平衡问题

负载平衡问题:

现有n个仓库,初始状态下每个仓库中有一些货物,且各仓库中数量不等。仓库只能向相邻的仓库搬运货物。现在想要让各个仓库的货物都相等,请求出最小的搬运量。输入n,初始状况下每个仓库的货物量,输出最少搬运量(输入保证初始货物量的平均数为整数)。

这题有许多解法,比如说贪心。不过它是网络流24题,网络流也可以解。

这道题要求一个最小的值,想到费用流。题解里有许多很玄妙的建图方法,不过最暴力的建图莫过于:

设初始时仓库i中货物量为starti,它们的平均数为aver。每个仓库建一个点,另外建立源点S和汇点T。对于仓库i,首先从S连接一条容量为starti,费用为0的边,表示初始时这个仓库有这么多货物可以调用;再向T连接一条容量为aver,费用为0的边,表示最后这个仓库应该有这么多货物。这些都不算作搬运,因此费用都为0

接下来处理仓库间的边。由于我们是暴力做法,因此按题意,每个仓库向相邻仓库各建一条容量为+∞,费用为1的边,表示这个仓库可以向两侧输送任意的货物,但这种搬运计入总代价,所以费用为1。写代码的时候可以把+∞改为货物总量。

由于最大流已经确定为货物总量(因为连向T的边只有n条容量为aver的边,S的出边的容量和也为货物总量),因此可以在这张图上跑费用流,最小费用就是题目的解。这种方法虽然比较直白,但是也是可以AC的,十分具有灵性~

仓库间建图的方法可以改进一下:显然过多的搬运是不必要的(即不需要先把一个仓库搬到aver以下,再把它调整回aver),但是可能会有中间转运(比如其它仓库已经达到aver,只剩下两个仓库,一个多一个少,但它们又不相邻,显然需要经过中间仓库来转运),因此可以从每个初始值大于aver的仓库向每个初始值小于aver的仓库连边,容量依然是+∞,费用为它们之间的距离(即中间经过的转运仓库数+1)来模拟中间转运的过程。

这样建图依然跑费用流即可。

对于n≤100的数据来讲,两种建图没什么差别~

#include<cstdio>
#include<cstring>
#define min(a,b) (a<b?a:b)
#define MXN 100+2
#define MXM 500+5
int n,cost_ans,flow_ans;
int start[MXN];
int aver;
int v[2*MXM],flow[2*MXM],cost[2*MXM],fst[MXN],nxt[2*MXM];
int dis[MXN],prev[MXN],prev_edge[MXN];
bool vis[MXN];
int etop;
int S,T;
int queue[MXN*MXN],head,tail;
void AddEdge(int x,int y,int z,int w){
    v[etop]=y;
    flow[etop]=z;
    cost[etop]=w;
    nxt[etop]=fst[x];
    fst[x]=etop++;
    v[etop]=x;
    flow[etop]=0;
    cost[etop]=-w;
    nxt[etop]=fst[y];
    fst[y]=etop++;
    return;
}
bool EKSPFA(){
    memset(vis,0,sizeof(vis));
    memset(dis,0x7f,sizeof(dis));
    int x,y;
    head=tail=0;
    queue[tail++]=S;
    dis[S]=0;
    vis[S]=true;
    while(head<tail){
        x=queue[head++];
        for(y=fst[x];y!=-1;y=nxt[y]){
            if(flow[y]>0 && dis[v[y]]>dis[x]+cost[y]){
                if(vis[v[y]]==false){
                    vis[v[y]]=true;
                    queue[tail++]=v[y];
                }
                dis[v[y]]=dis[x]+cost[y];
                prev[v[y]]=x;
                prev_edge[v[y]]=y;
            }
        }
        vis[x]=false;
    }
    if(dis[T]==0x7f7f7f7f) return false;
    else return true;
}
void MinCostFlow(){
    cost_ans=flow_ans=0;
    int f;
    while(EKSPFA()){
        f=0x6fffffff;
        for(int i=T;i!=S;i=prev[i]){
            f=min(f,flow[prev_edge[i]]);
        }
        flow_ans+=f;
        cost_ans+=f*dis[T];
        for(int i=T;i!=S;i=prev[i]){
            flow[prev_edge[i]]-=f;
            flow[prev_edge[i]^1]+=f;
        }
    }
    return;
}
int Next(int x){
    if(x+1<=n) return x+1;
    else return 1;
}
int Prev(int x){
    if(x-1>0) return x-1;
    else return n;
}
int main(){
    memset(fst,-1,sizeof(fst));
    memset(nxt,-1,sizeof(nxt));
    scanf("%d",&n);
    S=0,T=n+1;
    etop=aver=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&start[i]);
        AddEdge(S,i,start[i],0);
        aver+=start[i];
    }
    aver/=n;
    for(int i=1;i<=n;i++){
        AddEdge(i,T,aver,0);
        AddEdge(i,Next(i),aver*n,1);
        AddEdge(i,Prev(i),aver*n,1);
    }
    MinCostFlow();
    printf("%d
",cost_ans);
    return 0;
}
代码
原文地址:https://www.cnblogs.com/halifuda/p/8386450.html