POJ3228 并查集或二分最大流枚举答案

忘记写题意了。这题题意:给出每个地点的金矿与金库的数量,再给出边的长度。求取最大可通过边长的最小权值使每个金矿都能运输到金库里。

这题和之前做的两道二分枚举最大流答案的问法很相识,但是这里用最大流速度不够快,所以用了并查集来判断。

方法:边排序后,每次边判断为,将两点合集,若合集中满足金库数量大于金矿数量  则该合集满足条件。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 40000
int n,m,ans,in,out;
int fa[500],f[500];
bool flag;
struct Edge{
	int a,b,len;
}edge[MAXN];
int find(int x)
{
	if(x!=fa[x])
		fa[x]=find(fa[x]);
	return fa[x];
}
bool cmp(Edge a,Edge b)
{
	return a.len<b.len;
}
int main()
{
	while(scanf("%d",&n)!=EOF&&n)
	{
		int num=0;          //不满足的集合个数
		memset(f,0,sizeof(f));
		for(int i=0;i<n;i++)
		{
			scanf("%d",&in);
			f[i+1]-=in;
		}
		for(int i=0;i<n;i++)
		{
			scanf("%d",&out);
			f[i+1]+=out;
			if(f[i+1]<0)num++;
		}
		scanf("%d",&m);
		int a,b,c;
		for(int i=0;i<m;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			edge[i].a=a;
			edge[i].b=b;
			edge[i].len=c;
		}
		sort(edge,edge+m,cmp);
		int f1,f2;
		for(int i=0;i<=n;i++)
			fa[i]=i;
		flag=false;
		for(int i=0;i<m;i++)  //从小枚举边长  最多40000次
		{
			f1=find(edge[i].a);
			f2=find(edge[i].b);
			if(f1!=f2)
				{
					fa[f1]=f2;   //合并时 进行判断   //f2成为父节点
					//以f2为父节点的集合  加上以f1为父节点的结合的f 当此集合满足时
					f[f2]+=f[f1];//更新集合若满足条件后  进行操作
					if(f[f2]>=0)
					{
						if(f[f2]-f[f1]<0||f[f1]<0)   //表示解决一个
							num--;
					}
					else if(f[f2]<0)
					{
						if(f[f2]-f[f1]<0&&f[f1]<0)  //两个问题合并成一个
							num--;
					}
					if(num==0)
					{
							flag=true;
							ans=edge[i].len;
					}
				}
			if(flag)
				break;
		}
		if(flag)
			printf("%d\n",ans);
		else
			printf("No Solution\n");
	}
	return 0;
}


posted @ 2014-02-28 19:29  amourjun  阅读(197)  评论(0编辑  收藏  举报