【SSL_1896】家族---并查集

家族

Time Limit:10000MS Memory Limit:65536K
Total Submit:323 Accepted:155
Case Time Limit:1000MS

Description

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

Input

第一行:三个整数n,m,p,(n<=50000,m<=50000,p<=50000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Ai和Bi具有亲戚关系。接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。

Output

P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。

Sample Input

6 5 3
41 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6

Sample Output

Yes
Yes
No

思路

并查集模板题:
将每个人抽象成为一个点,数据给出M个边的关系,两个人是亲戚的时候两点间有一条边。
很自然的就得到了一个N个顶点M条边的图论模型。在图中一个连通块中的任意点之间都是亲戚。
对于最后的Q个提问,即判断所提问的两个顶点是否在同一个连通块中。
路径压缩
为了进一步增强堆寻找运算性能,路径压缩的措施被使用。在FIND(x)运算中,找到根节点y后,
我们再进行一次遍历从x到y的路径,并沿着路径改变所有节点指向父节点的指针,使它直接指向y。如下图:
在这里插入图片描述

int find(int x)
{
	if(x==fa[x]) return x;
	fa[x]=find(fa[x]);
	return fa[x];
}

输入边时,将边连接的两个端点的连通块连起来,即
合并集合
包含元素x和y的两个集合用它们的并集替换。并集的名字或者是原来的包含元素x那个集合的名字,
或者是原来包含元素y的那个集合的名字。
对于每个询问x、y,若xy的根节点相同,则属于相同连通块,则是亲戚。
并查集的存储结构:
在这里插入图片描述
数组fa[x]表示结点x的根节点,如上图。

void un(int x,int y)
{
	int fx=find(x),fy=find(y);
	fa[fy]=fx;					
}

代码

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long fa[50001],n,m,x,p,y;
int find(int x)//压缩路径
{
	if(x==fa[x]) return x;
	fa[x]=find(fa[x]);//递归,寻找根节点的同时压缩路径
	return fa[x];
}
void un(int x,int y) //合并集合
{
	int fx=find(x),fy=find(y);
	fa[fy]=fx;
}
int main(){
	scanf("%d%d%d",&n,&m,&p);
	for(int i=1;i<=n;i++)
	{
		fa[i]=i;//将每个节点的根初始化为本身
	}for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);//输入边
		un(x,y);//将边连接的两个端点的连通块连起来
	}for(int i=1;i<=p;i++){
		scanf("%d%d",&x,&y);
		if(find(x)!=find(y)) cout<<"No"<<endl;
		else cout<<"Yes"<<endl;
		//若xy的根节点相同则是亲戚,反之······
	}
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 黑客帝国 设计师:白松林 返回首页