题解 - P4016 负载平衡问题
题目传送门
题意:给定n个数,求最少移动(只能相邻)多少,可以使每个数相同。
网络流-最小费用流。
最大网络流保证方案正确性,最小费用保证可行。
我们考虑每个数减去平均值,若为正数,则从超级源点连一条a[i]距离为0的边,表示此点要分出a[i]的流。 反之,则连向超级汇点,表示要吸收a[i]的流,再按照题意相邻的边连一条无限流,价格为1的边即可。
code
#include<bits/stdc++.h>
using namespace std;
#define int long long
int fl[50000],dis[50000],last[50000],pre[50000];
int n,m,ans,a[50000],tot=1,S,T;
int head[50000],go[50000],nex[50000],w[50000],d[50000],maxflow,mincost,vis[50000];
void add(int u,int v,int flow,int cost)
{
nex[++tot]=head[u];
head[u]=tot;
go[tot]=v;
w[tot]=flow;
d[tot]=cost;
}
bool spfa()
{
memset(vis,0,sizeof(vis));
memset(dis,0x7f,sizeof(dis));
memset(fl,0x7f,sizeof(fl));
queue <int> q;
q.push(S);
dis[S]=0;
vis[S]=1;
pre[T]=-1;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=nex[i])
{
int v=go[i];
if(w[i]>0&&dis[v]>dis[u]+d[i])
{
dis[v]=dis[u]+d[i];
fl[v]=min(fl[u],w[i]);
pre[v]=u;
last[v]=i;
if(vis[v]==0)
{
vis[v]=1;
q.push(v);
}
}
}
}
return pre[T]!=-1;
}
signed main()
{
int sum=0;
cin>>n;
S=0,T=n+1;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
sum/=n;
for(int i=1;i<=n;i++)
{
a[i]-=sum;
}
for(int i=1;i<=n;i++)
{
if(a[i]>0) add(S,i,a[i],0),add(i,S,0,0);
if(a[i]<0) add(i,T,-a[i],0),add(T,i,0,0);
}
for(int i=1;i<=n;i++)
{
if(i!=1) add(i,i-1,1e9,1),add(i-1,i,0,-1);//注意建反边时流为0,代价为-dis。
if(i!=n) add(i,i+1,1e9,1),add(i+1,i,0,-1);
}
add(1,n,1e9,1),add(n,1,0,-1);
add(n,1,1e9,1),add(1,n,0,-1);
while(spfa())
{
// cout<<fl[T]<<" "<<dis[T]<<endl;
maxflow+=fl[T];
mincost+=dis[T]*fl[T];
int now=T;
while(now!=S)
{
w[last[now]]-=fl[T];
w[last[now]^1]+=fl[T];
now=pre[now];
}
}
cout<<mincost;
return 0;
}