P4016 负载平衡问题 [网络流]

传送门

原点向多出的点减边 , 费用为0 , 流量为多出个数

少个数的点向汇点建边 , 费用为0 , 流量为少的个数

相邻两个建边 , 费用为1 , 流量为inf , 然后跑最小费用最大流

#include<bits/stdc++.h>
#define N 105
#define M 10050
#define inf 0x3fffffff
using namespace std;
int first[N],next[M],to[M],w[M],c[M],tot=1;
int n,a[N],s,st,ed,dis[N],vis[N],from[N],froms[N];
void add(int x,int y,int z,int v){
//	cout<<x<<" "<<y<<" "<<z<<" "<<v<<endl;
	next[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z,c[tot]=v;
	next[++tot]=first[y],first[y]=tot,to[tot]=x,w[tot]=0,c[tot]=-v;
}
bool spfa(){
	queue<int> q; q.push(st);
	memset(dis,127,sizeof(dis)); dis[st] = 0; int Inf = dis[1];
	memset(vis,0,sizeof(vis)); vis[st] = 1;
	while(!q.empty()){
		int x = q.front(); q.pop(); vis[x] = 0;
		for(int i=first[x];i;i=next[i]){
			int t=to[i]; if(w[i] && dis[t]>dis[x]+c[i]){
				from[t] = x , froms[t] = i;
				dis[t] = dis[x] + c[i]; 
				if(!vis[t]) q.push(t), vis[t] = 1;
			}
		}
	} return (dis[ed]!=Inf);
}
int calc(){
	int u = ed , flow = inf;
	while(u!=st){
		flow = min(flow , w[froms[u]]);
		u = from[u];
	} u = ed;
	while(u!=st){
		w[froms[u]] -= flow;
		w[froms[u] ^ 1] += flow;
		u = from[u];
	} return flow;
}
int dinic(){int ans=0; while(spfa()) ans += calc() * dis[ed]; return ans;}
int main(){
	scanf("%d",&n); st=0,ed=n+1; 
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),s+=a[i];
	s/=n; for(int i=1;i<=n;i++){
		if(a[i]>s) add(st,i,a[i]-s,0);
		if(a[i]<s) add(i,ed,s-a[i],0);
	} for(int i=2;i<=n-1;i++){
		add(i,i-1,inf,1); add(i,i+1,inf,1);
	} add(1,2,inf,1),add(1,n,inf,1); add(n,n-1,inf,1); add(n,1,inf,1);
	printf("%d",dinic()); return 0; 
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值