目录
例题:找亲戚
概念引入
· 并查集核心思想:用一个元素代表集合;把集合比喻成帮派,帮主就是代表元素
· 最开始,大家各自为战,帮主就是自己(一个元素的集合,代表就是那个元素自己)
· 两个人比武,输方认赢方为帮主(两个元素合并后的集合,代表元素是被认作帮主那个)
· 两个帮派比武,输帮帮主带领小弟投降,认赢帮帮主为帮主(两个集合合为一个集合,代表元素是获胜帮的帮主)
· 最后这个集合,内部元素的关系是树状结构,以帮主为根节点
· 寻找集合代表元素(寻找根节点)的方法是:不断访问父节点,当父节点是自己时,说明 这是根结点了(树的知识:根结点的父节点是自己)
最简单代码
数据结构
二维数组 fa[MAX] 一个(MAX的值自己设定)
初始化
有 n 个人, 把二维数组 fa[1] ~ fa[n] 的值设置为下标
查询
对一个数字 x ,返回 x == fa[x] ? x : (fa[x] = find(fa[x]));
合并 x 和 y
有一个比较保险但是有一定时间复杂度的方法,令输入的 x ,y 等于自己查询出来的根结点,比较 x ,y 是否相等(比较是否同根)
如果不同根 ,fa[y] = x ,让一个根结点接入另一个根结点
例题:找亲戚
描述
或许你并不知道,你的某个朋友是你的亲戚。
他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。
如果能得到完整的家谱,判断两个人是否是亲戚应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家谱十分庞大,那么检验亲戚关系实非人力所能及。
在这种情况下,最好的帮手就是计算机。
为了将问题简化,你将得到一些亲戚关系的信息,如Marry和Tom是亲戚,Tom和Ben是亲戚,等等。
从这些信息中,你可以推出Marry和Ben是亲戚。
请写一个程序,对于我们的关于亲戚关系的提问,以最快的速度给出答案。
输入
输入由两部分组成。
第一部分以N,M开始。N为问题涉及的人的个数。这些人的编号为1,2,3,…,N。下面有M行,每行有两个数ai,bi,表示已知ai和bi是亲戚。
第二部分以Q开始。以下Q行有Q个询问,每行为ci,di,表示询问ci和di是否为亲戚。
数据范围
1≤N≤20000
1≤M≤10^6,
1≤Q≤10^6输出
对于每个询问ci,di,输出一行:若ci和di为亲戚,则输出“Yes”,否则输出“No”。
输入样例 1
10 7 2 4 5 7 1 3 8 9 1 2 5 6 2 3 3 3 4 7 10 8 9输出样例 1
Yes No Yes
#include<iostream>
using namespace std;
const int MAX = 200000 ;
int fa[MAX] ;
int find_fa(int per1){
return per1 == fa[per1] ? per1 : (fa[per1] = find_fa(fa[per1]));
}
int together(int per1 , int per2){
per1 = find_fa(per1) ;
per2 = find_fa(per2) ;
if(per1 != per2){
fa[per2] = per1 ;
}//不知道为什么加了这个判定是否是相同根的代码就过了啊。。。。
return 0 ;
}
int init(){
int persons = 0,times = 0;
scanf("%d%d" , &persons , ×) ;
for(int i = 1 ; i <= persons ; i++){
fa[i] = i ;
}
for(int i = 0 ; i < times ; i++){
int per1 = 0, per2 = 0;
scanf("%d%d" , &per1 ,&per2);
together(per1 , per2) ;
}
return 0;
}
int ask(){
int times = 0 ;
scanf("%d" ,×);
for(int i = 0 ; i < times ; i++){
int per1 = 0, per2 = 0;
scanf("%d%d" , &per1 ,&per2) ;
if(find_fa(per1) == find_fa(per2)){
cout << "Yes" << endl ;
}
else{
cout << "No" << endl ;
}
}
return 0;
}
int main(){
init() ;
ask() ;
}