洛谷P4016 负载平衡问题(费用流)

题目

https://www.luogu.com.cn/problem/P4016

思路

这题是网络流24题中的一道相对简单的题。既然是网络流题,我们先考虑怎么建图。

读入各仓库货物数\(a[i]\)求出平均值\(\frac{sum}{n}\),我们就知道每一个仓库需要调入/调出多少货物,令\(f(i)=a[i]-\frac{sum}{n}\)

新建一个源点S和一个汇点T,对于任意的\(i\),若\(f(i)>0\),说明该仓库要向其他仓库调出货物,我们加入一条从S到\(i\)的边,边权为0,容量为\(f(i)\)。可以看成本来没有东西,源点S通过这条边给了它\(f(i)\)个。它必须要把这\(f(i)\)个流走。

同理,若\(f(i)<0\),说明该仓库要从其他仓库调入货物,我们加入一条从\(i\)到T的边,边权为0,容量为\(-f(i)\)。可以看成本来是负的,只有满足了这条边,才说明有其他仓库流向它将其补为0。

至于环内的边,没有限制,随便流,一定是满足负载平衡的。将每一个点与其相邻点连边,边权为1,容量为无穷。

图片可以帮助理解:

跑一遍最小费用最大流就ok了。图片只是示例,写代码的时候不要忘记处理反向边。

代码

#include<cstdio>
#include<cstdlib>
#include<queue>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int n,fst[110],nxt[1000],cnt=0,a[101],book[110],dis[110],pre[110];
struct edge{
    int u,v,w,cap;
} e[1000];
void add(int x,int y,int z,int k){
    e[++cnt].u=x;
    e[cnt].v=y;
    e[cnt].w=z;
    e[cnt].cap=k;
    nxt[cnt]=fst[x];
    fst[x]=cnt;
}
int spfa(){
    int i;
    queue<int> q;
    memset(book,0,sizeof(book));
    memset(dis,inf,sizeof(dis));
    dis[0]=0;book[0]=1;
    q.push(0);
    while(!q.empty()){
        int p=q.front();
        for(i=fst[p];i;i=nxt[i]){
            if(e[i].cap<=0) continue;
            if(dis[e[i].v]>dis[p]+e[i].w){
                dis[e[i].v]=dis[p]+e[i].w;
                pre[e[i].v]=i;
                if(!book[e[i].v]){
                    book[e[i].v]=1;
                    q.push(e[i].v);
                }
            }
        }
        book[p]=0;
        q.pop();
    }
    if(dis[n+1]<inf) return 1;
    else return 0;
}
int inv(int x){
    if(x&1) return x+1;
    else return x-1;
}
int dinic(){
    int i,ans=0;
    while(spfa()){
        int flow=inf;
        for(i=n+1;i;i=e[pre[i]].u)
            flow=min(e[pre[i]].cap,flow);
        for(i=n+1;i;i=e[pre[i]].u){
            e[pre[i]].cap-=flow;
            e[inv(pre[i])].cap+=flow;
        }
        ans+=flow*dis[n+1];
    }
    return ans;
}
int main(){
    int i,j,x,sum=0,ans;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        sum+=a[i];
    }
    for(i=1;i<=n;i++){
        x=a[i]-sum/n;
        if(x<0){
            add(i,n+1,0,-x);
            add(n+1,i,0,0);
        }
        if(x>0){
            add(0,i,0,x);
            add(i,0,0,0);
        }
    }
    for(i=1;i<=n;i++){
        add(i,i%n+1,1,inf);
        add(i%n+1,i,-1,0);
        add(i,(i-2+n)%n+1,1,inf);
        add((i-2+n)%n+1,i,-1,0);
    }
    printf("%d",dinic());
    system("pause");
    return 0;
}
posted @ 2020-11-24 14:12  文艺平衡树  阅读(66)  评论(0编辑  收藏  举报