问题描述
有一个由n个沿环形铁路分布的仓库,每个仓库有一定的货物,某一个仓库可以往两边的仓库运送货物,求使n个仓库货物相等时的最小运输量。
怎么做?
这道题有很多做法,有贪心的,有二分的,有网络流的,其他的算法相信同学们可以在其他的题解上看到,所以在这里主要讲一下网络流的做法。其实这是一道最小费用最大流的很好的题。
解法
我们以一批货物从原始仓库到目标仓库所经过的仓库个数作为费用,以这批货物的大小作为流量,那么这时的运输量就是费用*流量。没错,如果想出了这个公式,我们就可以很快地解出这道题。
1.我们先拆点,因为如果只有一个点会很难建图。现在对于仓库i有i和i’两个点,i为贡献点,i’为需求点。
2.如果数量-平均值<0,那么这个点只可能是需求点,所以i’到t连一条流量为平均值-数量,费用为0。
3.如果数量-平均值>0,那么这个点只可能是贡献点,所以s到i连一条流量为数量-平均值,费用为0。
4.中间的点就i的贡献点到左右的需求点连一条流量为无限,费用为1的边,因为这批货物在这里出现新一次的转移。
5.有很多同学想问,你这样满足的只是两个相邻点之间的货物转移,不能满足跨点运输。对!所以我们在贡献点之间连一条流量为无限,费用为1的边,因为在这里,货物进行新一次的转移。
最后跑一遍最小费用最大流,把流量*费用的总和输出就行。
这里的最大流保证了每个仓库都满足要求,最小费用则保证了运输量最小。
贴代码,我的代码风可能会引起不适。
#include<cstdio>
#include<cstdlib>
#include<cstring>
struct edge{int x,y,next,c,cos;};
edge s[1010];
int n;
int p[110];
int tot=0;
int len=1;
int f[1010];
int st=1,ed=2;
int sum[1010],mmin[1010],ip[1010];
int first[1010];
int begin=0,end;
bool tf[1010];
int min(int x,int y)
{
return x<y?x:y;
}
void ins(int x,int y,int c,int cos)
{
len++;
s[len].x=x;s[len].y=y;s[len].next=first[x];first[x]=len;s[len].cos=cos;s[len].c=c;
len++;
s[len].x=y;s[len].y=x;s[len].next=first[y];first[y]=len;s[len].cos=-cos;s[len].c=0;
}
int SPFA(int &cost,int &flow)
{
memset(sum,63,sizeof(sum));
memset(mmin,63,sizeof(mmin));
memset(tf,false,sizeof(tf));
st=1,ed=2;
tf[begin]=true;
sum[begin]=0;
f[st]=begin;
while(st!=ed)
{
int x=f[st];
tf[x]=false;
for(int i=first[x];i!=0;i=s[i].next)
{
int y=s[i].y;
if(sum[y]>sum[x]+s[i].cos && s[i].c>0)
{
ip[y]=i;
mmin[y]=min(mmin[x],s[i].c);
sum[y]=sum[x]+s[i].cos;
if(tf[y]==false)
{
tf[y]=true;
f[ed]=y;
ed++;
}
}
}
st++;
}
if(sum[end]==1061109567) return 0;
flow+=mmin[end];
cost+=mmin[end]*sum[end];
int u=end;
while(u!=begin)
{
int i=ip[u];
s[i].c-=mmin[end];
s[i^1].c+=mmin[end];
u=s[i].x;
}
return 1;
}
int cost_flow()
{
int cost=0,flow=0;
while(SPFA(cost,flow));
return cost;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&p[i]);
tot+=p[i];
}
tot/=n;
end=2*n+1;
for(int i=1;i<=n;i++)
{
p[i]-=tot;
if(p[i]<0)
ins(i+n,end,-p[i],0);
if(p[i]>0)
ins(begin,i,p[i],0);
if(i-1>=1)
{
ins(i,i-1,1e9,1);
ins(i,i-1+n,1e9,1);
}
else
{
ins(i,n+n,1e9,1);
ins(i,n,1e9,1);
}
if(i+1<=n)
{
ins(i,i+1,1e9,1);
ins(i,i+1+n,1e9,1);
}
else
{
ins(i,n+1,1e9,1);
ins(i,1,1e9,1);
}
}
int ans=cost_flow();
printf("%d\n",ans);
}