网络流24题之负载平衡问题
题目链接:传送门
初次看这道题是不是发现这道题和均分纸牌很像,只是一个是环一个是链而已,所以这道题明显可以贪心啊,但是因为这是网络流24题,所以还是把它当做网络流的题目来做吧
这是一道费用流的题目,首先老规矩建立一个源点,汇点
为什么要建啊?,问这个问题有两种可能性
1.太强,请移步至传送门
2.太弱,请移步至传送门
怎么连接汇点和源点呢?贪心的想一下,要使所有仓库数量相等,所以应将多的仓库运往少的仓库,所以多的仓库应该贡献,连向源点。少的仓库应该得到,连向汇点,且费用为0。然后相邻节点有一条流量为inf,费用为1的边(注意这里为无向边)
再来回到开始说所得这是一个环不是一个链,所以要特殊处理一下1和n节点
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=10001;
queue<int> q;
int dis[N],f[N],pre[N],fa[N],head[N],cnt,n,m,s,t,x,y,z,w,ans,ans1,b[N];
struct node {
int to,next,v,w;
} a[100001];
void add(int x,int y,int c,int v) {
a[++cnt].to=y;
a[cnt].next=head[x];
a[cnt].v=c;
a[cnt].w=v;
head[x]=cnt;
}
int spfa() {
memset(dis,127,sizeof(dis));
memset(f,0,sizeof(f));
q.push(s);
f[s]=1;
int inf=dis[1];
dis[s]=0;
while(!q.empty()) {
int now=q.front();
q.pop();
f[now]=0;
for(int i=head[now]; i; i=a[i].next) {
int v=a[i].to;
if(dis[v]>dis[now]+a[i].w&&a[i].v) {
dis[v]=dis[now]+a[i].w;
fa[v]=now;
pre[v]=i;
if(!f[v])
q.push(v),f[v]=1;
}
}
}
if(dis[t]!=inf)
return 1;
return 0;
}
void answer() {
while(spfa()) {
int minx=2147483647;
for(int i=t; i!=s; i=fa[i])
minx=min(minx,a[pre[i]].v);
ans1+=dis[t]*minx;
for(int i=t; i!=s; i=fa[i]) {
a[pre[i]].v-=minx;
if(pre[i]%2)
a[pre[i]+1].v+=minx;
else
a[pre[i]-1].v+=minx;
}
}
}
int main() {
scanf("%d",&n),ans=0,t=n+1;
for(int i=1;i<=n;i++)
scanf("%d",&b[i]),ans+=b[i];
for(int i=1;i<=n;++i)
b[i]-=ans/n;
for(int i=1;i<=n;++i){
if(b[i]>0)
add(s,i,b[i],0),add(i,s,0,0);
else if(b[i]<0)
add(i,t,-b[i],0),add(t,i,0,0);
}
for(int i=1;i<n;i++)
add(i,i+1,100000000,1),add(i+1,i,0,-1),add(i+1,i,10000000,1),add(i,i+1,0,-1);
add(n,1,ans,1),add(1,n,0,-1);
add(1,n,ans,1),add(n,1,0,-1);
answer();
printf("%d",ans1);
return 0;
}