P4016 负载平衡问题 题解

题目大意

一个环上有(n)个仓库,每个仓库存储的货物数量不等,如何用最少搬运量可以使 (n) 个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。

P4016 负载平衡问题

问题解决

貌似这个就是一道奥数题,但是众所周知,(OI)不是(MO)我们考虑用(OI)的方法来解决

每个点中的货物运输很难处理,但是总的数是不变的,而且(n)非常小,我们就可以考虑用费用流来解决这个问题

我们先算出最终状态,也就是(avg= sum /n)

  • 建立一个超级原点,和超级汇点

  • 对于一个(a[i]-avg>0)的仓库,也就是说需要将货物从这里运走,所以从超级原点建一条到(a[i])的边,流量为(a[i]-avg),费用为(0)

  • 对于一个(a[i]-avg<0)的仓库,也就是说需要货物运进来,所以建一条从(a[i])到超级汇点的边,流量为(a[i]-avg),费用为(0)

  • (a[i])之间可以相互运,所以之间建一条边,流量为(INF),费用为(1),注意是环

代码实现

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=105,maxe=605,INF=0x3f3f3f3f;
int N,avg,a[maxn],Ans;
int lnk[maxn],nxt[maxe],son[maxe],w[maxe],c[maxe],cnt=1,st,ed,x[maxn];
int dis[maxn],vis[maxn];
int mincost;
inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
void add_e(int x,int y,int z,int cost){son[++cnt]=y;w[cnt]=z;c[cnt]=cost;nxt[cnt]=lnk[x];lnk[x]=cnt;}
void add_edge(int x,int y,int z,int cost){add_e(x,y,z,cost);add_e(y,x,0,-cost);}
int spfa(){
	queue<int> q;
	memset(dis,INF,sizeof dis);
	memset(vis,0,sizeof vis);
	dis[st]=0;q.push(st);
	while(!q.empty()){
		int cur=q.front();q.pop();vis[cur]=0;
		for(int j=lnk[cur];j;j=nxt[j]){
			if(w[j]>0&&dis[son[j]]>dis[cur]+c[j]){
				dis[son[j]]=dis[cur]+c[j];
				if(!vis[son[j]]){vis[son[j]]=1;q.push(son[j]);}
			}
		}
	}
	return dis[ed]!=INF;
}
int DFS(int x,int delta){
	if(x==ed)return delta;
	vis[x]=1;int flow=0;
	for(int j=lnk[x];j;j=nxt[j]){
		if(!vis[son[j]]&&w[j]>0&&dis[son[j]]==dis[x]+c[j]){
			int tmp=DFS(son[j],min(delta-flow,w[j]));
			if(tmp){
				mincost+=(c[j]*tmp);
				w[j]-=tmp;w[j^1]+=tmp;flow+=tmp;
			}
		}
	}
	vis[x]=0;
	return flow;
}
int Dinic(){
	int maxflow=0;
	while(spfa()){
		while(1){
			int temp=DFS(st,INF);
			if(!temp)break;
			maxflow+=temp;
		}
	}
	return maxflow;
}
int main(){
	N=read();for(int i=1;i<=N;i++) a[i]=read(),avg+=a[i];avg/=N;
	st=0;ed=N+1;
	for(int i=1;i<=N;i++){x[i]=a[i]-avg;if(x[i]>0)add_edge(st,i,x[i],0);if(x[i]<0)add_edge(i,ed,-x[i],0);}
	for(int i=1;i<=N;i++){if(i!=1)add_edge(i,i-1,INF,1);if(i!=N)add_edge(i,i+1,INF,1);}
	add_edge(1,N,INF,1);add_edge(N,1,INF,1);
	Ans=Dinic();
	printf("%d
",mincost);
	return 0;
}
原文地址:https://www.cnblogs.com/martian148/p/15180117.html