Description
由于省赛的失利(难以理喻的失常),gxg心情十分沉痛—不能为校争光,也辜负了老师的一番陪养。除此以外更多的是郁闷,但gxg知道大学的大门不会因为自己的郁闷而为自己打开,所以一定要振作起来。为了排遣郁闷,gxg开始玩起了智力游戏。
游戏是这样子的:
n个盒子被放成一圈,每个盒子按顺时针编号为1到n,(1<=n<=1000)。每个盒子里都有一些球,且所有盒子里球的总数不超过n。
这些球要按如下的方式转移:每一步可以将一个球从盒子中取出,放入一个相邻的盒子中。目标是使所有的盒子中球的个数都不超过1。
任务
• 从文件d.in中读入盒子的个数和每个盒子中球的个数
• 计算最少的步数是每个盒子中的球的个数不超过1
• 将结果写入文件d.out.
Input
输入文件第一行是一个整数n,表示盒子的个数。以后n行,每行中有一个非负整数,表示每个盒子中球的数目。
Output
输出文件包含一个数:达到目标所需要的最少步数。
Sample Input
12
0
0
2
4
3
1
0
0
0
0
0
1
Sample Output
19
Solution
这题的数据范围一看就很小,心里很开心,果断上贪心……
别提了,直接Wrong Answer!~~~原因是环的存在。
看了题解,才恍然大悟——最小费用最大流!
设源点和汇点,每个点向旁边的两个点(注意是环!)连一条容量为 0 、费用为 1 的边,
之后源点向每个点都连一条容量为 该点初始球个数 、费用为 0 的边,
每个点都再向汇点连一条容量为 1 、费用为 0 的边。
这样一个神奇的图就构好啦!接着跑一遍最小费用最大流,输出最小费用即可。
这可以用 SPFA 实现,每次按最小费用跑一次最短路,更新网络并累加,循环操作即可。
Code
#include<cstdio>
using namespace std;
const int N=2003,Mx=2e9;
int n,tot=1,ans;
int first[N],next[N*4],en[N*4],f[N*4],v[N*4];
int que[N*10],dis[N],g[N];
bool bz[N];
inline int read()
{
int data=0; char ch=0;
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
return data;
}
inline int min(int x,int y)
{
return x<y?x:y;
}
inline void link(int x,int y,int z,int p)
{
next[++tot]=first[x];
first[x]=tot;
en[tot]=y;
f[tot]=z;
v[tot]=p;
}
inline void insert(int x,int y,int z,int p)
{
link(x,y,z,p);
link(y,x,0,-p);
}
inline bool spfa()
{
for(int i=1;i<=n+1;i++) dis[i]=Mx;
int l=que[1]=0,r=1;
while(l<r)
{
int now=que[++l];
bz[now]=false;
for(int i=first[now];i;i=next[i])
if(f[i] && dis[now]+v[i]<dis[en[i]])
{
dis[en[i]]=dis[now]+v[g[en[i]]=i];
if(!bz[en[i]]) bz[que[++r]=en[i]]=true;
}
}
return dis[n+1]<Mx;
}
inline void work()
{
int sum=Mx;
for(int i=n+1;i;i=en[g[i]^1]) sum=min(sum,f[g[i]]);
for(int i=n+1;i;i=en[g[i]^1])
{
f[g[i]]-=sum;
f[g[i]^1]+=sum;
ans+=v[g[i]]*sum;
}
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
{
int x=read();
insert(0,i,x,0);
insert(i,n+1,1,0);
int l=i>1?i-1:n,r=i<n?i+1:1;
insert(i,l,Mx,1);
insert(i,r,Mx,1);
}
while(spfa()) work();
printf("%d",ans);
return 0;
}