spoj (BOXS IT)371(最小费用最大流)

题目链接: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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值