SPOJ BOXES

There are n boxes on the circle. The boxes are numbered from 1 to n
(1<=n<=1000) in clock wise order. There are balls in the boxes, and
the number of all the balls in the boxes is not greater than n.

The balls should be displaced in such a way that in each box there
remains no more than one ball. In one move we can shift a ball from
one box to one of it’s neighboring boxes.

Write a program that: reads from the standard input the number of
boxes n and the arrangement of balls in the boxes, computes the
minimal number of moves necessary to displace the balls in such a way
that in each box there remains no more than one ball, writes the
result in the standard output. Input

The first line of the input file contains an integer t representing
the number of test cases (t<=20). Then t test cases follows. Each test
case has the following form:

The first line contains one positive integer n - the number of boxes
The second line contains n nonnegative integer separated by single spaces. The i-th number is the number of balls in the i-th box. 

Output

For each test case, output one nonnegative integer - the number of
moves necessary to displace the balls in such a way that in each box
there remains no more than one ball.

费用流。
如果一个位置 u 没有石子,它可以解决另外一个石子,连边(u,t,1,0)。如果一个位置 u 的石子数au多于一个,他需要别的位置解决他。连边 (s,u,au1,0) 。相互之间解决是有代价的,对相邻的 u,v 连边 (u,v,oo,1)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int s=1005,t=1006,oo=0x3f3f3f3f;
int fir[1010],ne[20010],to[20010],w[20010],c[20010],
in[1010],que[1010],len[1010],minw[1010],fa[1010],
n,num;
void add(int u,int v,int x,int y)
{
    num++;
    ne[num*2]=fir[u];
    fir[u]=num*2;
    to[num*2]=v;
    w[num*2]=x;
    c[num*2]=y;
    ne[num*2+1]=fir[v];
    fir[v]=num*2+1;
    to[num*2+1]=u;
    w[num*2+1]=0;
    c[num*2+1]=-y;
}
void init()
{
    int x;
    scanf("%d",&n);
    num=0;
    memset(fir,0,sizeof(fir));
    for (int i=0;i<n;i++)
    {
        scanf("%d",&x);
        if (!x) add(i,t,1,0);
        if (x>1) add(s,i,x-1,0);
        add(i,(i+1)%n,oo,1);
        add(i,(i-1+n)%n,oo,1);
    }
}
void check()
{
    for (int i=0;i<=t;i++)
        for (int j=fir[i];j;j=ne[j])
            if (w[j]) printf("%d->%d:%d,%d\n",i,to[j],w[j],c[j]);
}
bool spfa()
{
    /*check();
    printf("\n\n\n");*/
    int hd=0,tl=1,u,v,mod=n+5;
    que[0]=s;
    in[s]=1;
    memset(len,0x3f,sizeof(len));
    len[s]=0;
    memset(minw,0,sizeof(minw));
    minw[s]=oo;
    while (hd!=tl)
    {
        u=que[hd++];
        hd%=mod;
        for (int i=fir[u];i;i=ne[i])
            if (len[u]+c[i]<len[v=to[i]]&&w[i])
            {
                len[v]=len[u]+c[i];
                fa[v]=i;
                minw[v]=min(minw[u],w[i]);
                if (!in[v])
                {
                    in[v]=1;
                    que[tl++]=v;
                    tl%=mod;
                }
            }
        in[u]=0;
    }
    return minw[t];
}
int find()
{
    for (int i=fa[t];i;i=fa[to[i^1]])
    {
        w[i]-=minw[t];
        w[i^1]+=minw[t];
    }
    return minw[t]*len[t];
}
int main()
{
    int T,ans;
    scanf("%d",&T);
    while (T--)
    {
        init();
        //check();
        ans=0;
        while (spfa()) ans+=find();
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值