JZOJ 1533. 郁闷的gxg

10 篇文章 0 订阅
9 篇文章 0 订阅

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值