poj 3498(March of the Penguins)

题目链接:http://poj.org/problem?id=3498

题意简述:有n个冰块每个冰块上有ni只企鹅,并且每个冰块允许跳出的次数为mi,判定所有的企鹅能不能够跳到一块冰块上去,当冰块之间的距离小于等于d时企鹅才能从一个冰块跳到另一个冰块,若能所有企鹅能够跳到同一个冰块上,那么输出这些冰块(注意这里输出的时候是以0为参考),若没有这样的冰块满足条件则输出-1

自己不入流的分析方法:每个冰块上的企鹅,要流到一个特定的冰块上,每个冰块限制了能够流出的企鹅数,那么我们就可以这样建图新建源点s ,将冰块拆点u、u‘ ,s到u的权值为冰块上初始的企鹅数,u-u’的权值为该冰块允许流出的企鹅数,最后枚举冰块作为汇点


代码:

#include<iostream>
#include<cstdio>
#include <cstring>
using namespace std;

const int inf=0x3f3f3f3f;
const int N = 210;
const int E = 40000;
struct node
{
	int x,y,c,w,nxt;
}edge[E];

struct node1
{
	double x, y;
	int n, m;
}a[110];
int harsh[N];
int e,head[N];
int dep[N],que[N],cur[N];

void addedge(int u,int v,int c)
{
	edge[e].x=u;
	edge[e].y=v;
	edge[e].w=0;
	edge[e].nxt=head[u];
	edge[e].c=c;
	head[u]=e++;

	edge[e].x=v;
	edge[e].y=u;
	edge[e].w=0;
	edge[e].nxt=head[v];
	edge[e].c=0;
	head[v]=e++;
}

int maxflow(int n,int s,int t)
{
	int i,j,k,front,rear,top,min,res=0;
	while(1)
	{
		for(i=0;i<=2*n;i++)
		   if(harsh[i]==1)
			   dep[i]=-2;
		    else dep[i]=-1;
		front=0;
		rear=0;
		que[rear++]=s;
		dep[s]=0;
		while(front!=rear)
		{
			i=que[front++];
			for(j=head[i];j!=-1;j=edge[j].nxt)
				if(edge[j].c>edge[j].w&&dep[edge[j].y]==-1)
				{
					dep[edge[j].y]=dep[i]+1;
					que[rear++]=edge[j].y;
				
				}
		}
		if(dep[t]==-1)
			break;
		memcpy(cur,head,sizeof(head)); 
		for(i=s,top=0;;)
		{
			if(i==t)
			{
				min=inf;
				for(k=0;k<top;k++)
					if(min>(edge[que[k]].c-edge[que[k]].w))
					{
						min=edge[que[k]].c-edge[que[k]].w;
						front=k;
					}
				for(k=0;k<top;k++)
				{
					edge[que[k]].w+=min;
					edge[que[k]^1].w-=min;
				}
				res+=min;
				i=edge[que[top=front]].x;
				
			}
			for(j=cur[i];cur[i]!=-1;j=cur[i]=edge[cur[i]].nxt)
				if(dep[edge[j].y]==dep[i]+1&&edge[j].c>edge[j].w)
					break;
			if(cur[i]!=-1)
			{
				que[top++]=cur[i];
				i=edge[cur[i]].y;
			}
			else
			{
				if(top==0)
					break;
				dep[i]=-1;
				i=edge[que[--top]].x;
			}
		}
	}
	return res;
}

double dis(int i, int j)
{
	return (a[i].x-a[j].x)*(a[i].x-a[j].x) + (a[i].y -a[j].y)*(a[i].y-a[j].y);
}

int main ()
{
	int t;
	double d;
	int sum,n,i,j;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%lf",&n,&d);
		sum=0;
		for(i=1;i<=n;i++)
		{
			scanf("%lf%lf%d%d",&a[i].x,&a[i].y,&a[i].n,&a[i].m);
			sum+=a[i].n;
		}
        memset(head,-1,sizeof(head));
		e = 0;
		for(i=1;i<=n;i++)
		{
			addedge(0,i,a[i].n);
			addedge(i,i+n,a[i].m);
		}
        for(i=1;i<=n;i++)
			for(j=i+1;j<=n;j++)
				if(dis(i,j)<=d*d)
				{
					addedge(n+i,j,inf);
					addedge(j+n,i,inf);
				}
		memset(harsh,0,sizeof(harsh));
		int tag = 0;
		for(i=1;i<=n;i++)
		{
			harsh[i+n]=1;
			int flow = maxflow(n,0,i);
			if(flow==sum)
			{
				tag = 1;
				printf("%d ",i-1);
			}
			harsh[i+n]=0;
			for(j=0;j<e;j++)
		    	edge[j].w=0;
		}
		if(tag==0)
			printf("-1\n");
		else printf("\n");
	}
	return 0;
}
		
			

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值