并查集初步

并查集模板:

适用范围:

对数据进行合并,查询某个数据的祖先,建立数据间的联系。

int fathe[mx]   //根节点
int rank[x]    //树的高度
void ini()
{
	for(int i=0;i<=n;i++){
		father[i]=i;
		rank[i]=0;
	}
}
int find(int x)
{
	if(father[x]==x)
	  return x;
	else
	  father[x]=find(father[x]);    //合并根节点,路径压缩
}
int unite(int x,int y)
{
	int a=find(x);
	int b=find(y);
	if(rank[x]<rank[y])
	  father[x]=y;   
	else
	{
		father[y]=x;         //向左合并
		if(rank[x]==rank[y])
		   rank[x]++;        //增加高度
	}
}

例题:

(1):Wireless Network
题意: 有n台电脑,电脑的覆盖范围为k米,在覆盖范围内任意两台电脑可以互联,现在所有的电脑都损坏了,给你这n台电脑的坐标,和两种操作:O表示修复电脑p,S表示检测两台电脑是否可以互联。
思路: 用结构体数组存贮电脑的位置坐标,用数组存储所有以修复电脑的序号,当执行完修复操作时,将当前电脑与所有以修复电脑进行合并操作(合并前判定两台电脑间的距离)。当执行检查操作时 ,判断两台电脑是否为同一组。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct stu
{
	double x;
	double y;
}f[1010];
int n,book[1010],num,fa[1010],k;
void ini()
{
	for(int i=0;i<=1005;i++)
	  fa[i]=i;
	memset(book,0,sizeof(book));
}
int find(int x)
{
	if(fa[x]==x)
	  return x;
	else
	  return fa[x]=find(fa[x]);    //更新根节点
}
void unite(int x,int y)
{
	int a,b;
	a=find(x);
	b=find(y);
	if(a!=b)
       fa[b]=a;    //合并根节点
}
int  check(int x,int y)   //判断距离
{
     return (f[x].x-f[y].x)*(f[x].x-f[y].x)+(f[x].y-f[y].y)*(f[x].y-f[y].y);
}
int main()
{
	memset(f,0,sizeof(f));
	char ch;
	int x,y;
	cin>>n>>k;
	ini();
	for(int i=1;i<=n;i++)
		scanf("%lf%lf",&f[i].x,&f[i].y);
	getchar(); 
	while(scanf("%c",&ch)!=EOF)
	{
		if(ch=='O')
		{
			scanf("%d",&x);
			for(int i=0;i<num;i++){
				if(check(book[i],x)<=k*k)
				  unite(book[i],x);    //合并
		//		  cout<<find(x)<<endl;
			}
			book[num++]=x;   //存储以修复的电脑序号
		}
		else if(ch=='S')
		{
			scanf("%d%d",&x,&y);
		//	cout<<find(3)<<endl;
		//	cout<<find(2)<<" "<<find(1)<<" "<<find(4)<<endl;
			if(find(x)==find(y))
				cout<<"SUCCESS"<<endl;
			else
				cout<<"FAIL"<<endl;
		}
		getchar();
	}
	return 0;
} 

(2)The Suspects

题意: 给定多个学生组,每组有k个学生,当每一组有一个学生可能患病则该组所有学生都患病,初始只有0号学生可能患病,求可能患病学生数。
思路: 标准的并查集,记录每组的第一个学生,然后其他学生与其合并,最后记录所有学生中与零号学生为同一组的就可以。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> q[510];
int n,m,k,f[30010];
void ini()     //并查集模板
{
	for(int i=0;i<=n;i++)
	   f[i]=i;
}
int find(int x)
{
	if(f[x]==x)
	  return x;
	else
	  return f[x]=find(f[x]);
}
void unite(int x,int y)
{
	int a=find(x);
	int b=find(y);
	if(a!=b)
	  f[b]=a;
}
int main()
{
	while(scanf("%d%d",&n,&m)&&n!=0||m!=0)
	{
		int ans=0;
		ini();
		for(int i=0;i<m;i++){
			int x,t;
			scanf("%d%d",&k,&t);
			for(int j=0;j<k-1;j++){
				scanf("%d",&x);
				unite(t,x);
		    }
		}
		for(int i=0;i<=n;i++){
			if(find(i)==find(0))    //判断是否有相同根节点
			  ans++;
		}
		cout<<ans<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值