题目链接:http://www.spoj.pl/status/
题意描述:有n个盒子排成一个圈,每个盒子开始有x个球,且所有盒子中球的总和不超过n,现在移动盒子中的球使得每个盒子最多有1个球,移动只能在相邻的盒子之间进行,求最少要移动多少次满足要求(每个盒子最多只能有一个球)
分析: 最小费用最大流题目,建图: 源点到每个盒子连边,容量为盒子中求的个数,费用为0,每个盒子向汇点连边,容量为1,费用为0,相邻的盒子之间连边容量为无穷大,费用为1,求最小费用最大流即为结果
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int inf = 0x3fffffff;
const int N = 1010;
const int E = 20000;
struct node
{
int y, nxt,cost,w;
}edge[E];
int head[N],e;
int dist[N],pre[N],vis[N],que[N];
void addedge(int x,int y, int w, int cost )
{
edge[e].y = y;
edge[e].w = w;
edge[e].cost = cost;
edge[e].nxt = head[x];
head[x]=e++;
edge[e].y = x;
edge[e].w = 0;
edge[e].cost = -cost;
edge[e].nxt = head[y];
head[y] = e++;
}
int minCmaxF(int s ,int t)
{
int c=0, f,r,min,p,i;
int u, v;
while(1)
{
for( i=s;i<=t;i++)
{
dist[i]=inf;
vis[i]=false;
}
que[0]=s;
vis[s]=true;
dist[s]=0;
f = 0;r = 1;
while(f!=r)
{
u = que[f];
f = (f+1)%N;
for(i=head[u];i!=-1;i=edge[i].nxt)
{
v = edge[i].y;
if(edge[i].w>0&&dist[v]>dist[u]+edge[i].cost)
{
dist[v]=dist[u]+edge[i].cost;
pre[v]=i;
if(vis[v]==false)
{
vis[v]=true;
que[r]=v;
r = (r+1)%N;
}
}
}
vis[u]=false;
}
if(dist[t]==inf)
break;
min = inf;
for(u=t;u!=s;u=edge[pre[u]^1].y)
{
p = pre[u];
if(min > edge[p].w)
min = edge[p].w;
}
for(u=t;u!=s;u=edge[pre[u]^1].y)
{
p=pre[u];
edge[p].w -= min;
edge[p^1].w+=min;
c += edge[p].cost*min;
}
}
return c;
}
int main ()
{
int t;
int n, i,c;
scanf("%d",&t);
while (t--)
{
scanf("%d",&n);
memset(head,-1,sizeof(head));
e = 0;
for(i=1;i<=n;i++)
{
scanf("%d",&c);
addedge(0,i,c,0);
addedge(i,n+1,1,0);
}
for(i=1;i<n;i++)
{
addedge(i,i+1,inf,1);
addedge(i+1,i,inf,1);
}
addedge(n,1,inf,1);
addedge(1,n,inf,1);
int x = minCmaxF(0,n+1);
printf("%d\n",x);
}
return 0;
}