【题目链接】
【题目考点】
1. 并查集
【解题思路】
每组互相是亲戚的人构成一个集合,如果两人是亲戚,那么调用merge函数让两人所在的集合合并,。
并查集中,每个集合由一个树来表示,树的根结点代表一个集合。find函数可以返回一个结点所在集合的根结点。
对于多组询问,判断两人是否在一个集合中,即为判断两人所在集合的根结点是否相同。
【题解代码】
解法1:并查集
- 写法1:递归查询+路径压缩,简单合并
#include<bits/stdc++.h>
using namespace std;
#define N 5005
int fa[N];
void initFa(int n)
{
for(int i = 1; i <= n; ++i)
fa[i] = i;
}
int find(int x)
{
if(x == fa[x])
return x;
else
return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
fa[find(x)] = find(y);
}
int main()
{
int n, m, p, a, b;
cin >> n >> m >> p;
initFa(n);
for(int i = 1; i <= m; ++i)
{
cin >> a >> b;
merge(a, b);
}
while(p--)
{
cin >> a >> b;
cout << (find(a) == find(b) ? "Yes" : "No") << endl;
}
return 0;
}
- 写法2:非递归查询,按秩合并
#include<bits/stdc++.h>
using namespace std;
#define N 5005
int fa[N], rk[N];//fa[i]:结点i的双亲结点 rk[i]:以结点i为根结点的树的秩
void initFa(int n)
{
for(int i = 1; i <= n; ++i)
fa[i] = i;
}
int find(int x)//非递归查询与路径压缩
{
int k, t, r;//r:根结点
k = r = x;
while(r != fa[r]) //查找根结点
r = fa[r]; //找到根结点,用r记录
while(k != r)//路径压缩:将从x到r的整条路径上的结点的父结点都设为r
{
t = fa[k]; //用t暂存k的父结点
fa[k] = r; //fa[k]指向根结点
k = t; //k指向暂存的父结点
}
return r; //返回根结点
}
void merge(int i, int j)//按秩合并i结点和j结点所在的集合
{
int x = find(i), y = find(j);//先找到两个根结点
if(x == y) return;//根结点相同,就不用合并了
if(rk[x] < rk[y])//高度低的树作为高度高的树的子树
fa[x] = y;
else if(rk[x] > rk[y])
fa[y] = x;
else//如果高度相同,则新的树的高度+1
{
fa[x] = y;
rk[y]++;
}
}
int main()
{
int n, m, p, a, b;
cin >> n >> m >> p;
initFa(n);
for(int i = 1; i <= m; ++i)
{
cin >> a >> b;
merge(a, b);
}
while(p--)
{
cin >> a >> b;
cout << (find(a) == find(b) ? "Yes" : "No") << endl;
}
return 0;
}