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\n",mincost);
	return 0;
}
posted @ 2021-08-24 14:19  Martian148  阅读(42)  评论(0编辑  收藏  举报