资料
例题
例题链接
题目背景
若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
题目描述
规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。
输入格式
第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。
以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Mi和Mj具有亲戚关系。
接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
输出格式
P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。
测试数据
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
题解(模板)
#include<iostream>
using namespace std;
#define N 1000
int parent[N], depth[N];
//初始化并查集
void init(int n) {
for (int i = 0; i <= n; i++) {
parent[i] = i; // 初始定义各个结点都为根节点
depth[i] = 1; // 初始定义树的深度都为1(即树结点数量)
}
}
// **********优化点二**********
// 不优化的find
//int find(int i) {
// while (i != parent[i]) {
// i = parent[i]; // 向上找父结点
// }
// return i;
//}
// 使用方式一优化,将查找节点直接链接到根节点上
//int find(int i) {
// if (i != parent[i]) {
// parent[i] = find(parent[i]); // 递归的一个过程
// }
// return parent[i];
//}
// 使用方式二优化 不断将父亲节点修改为爷爷节点
//int find(int i) {
// while (i != parent[i]) {
// parent[i] = parent[parent[i]];
// i = parent[i]; // 向上找父结点
// }
// return i;
//}
//可简写成:
int find(int i){
return i == parent[i] ? i : (parent[i] = find(parent[i]));
}
bool connected(int p, int q) {
p = find(p);
q = find(q);
return p == q;
}
//按秩合并
void unionNode(int p, int q) {
p = find(p);
q = find(q);
// **********优化点一**********
if (p == q) // 根节点相同
return;
else if (depth[p] > depth[q]) {
parent[q] = p; // p的深度更大,将q作为子树
}
else if(depth[p] < depth[q]){
parent[p] = q; // q的深度更大,将p作为子树
}
else {
parent[q] = p; // 两棵树的深度相同,随意合并 ,但是此时需要更新树的深度
depth[p]++;
}
}
int main() {
int n, m, p, temp1, temp2;
cin >> n >> m >> p;
init(n);//初始化并查集
for (int i = 0; i < m; i++) {
cin >> temp1 >> temp2;
unionNode(temp1, temp2);//合并
}
for (int i = 0; i < p; i++) {
cin >> temp1 >> temp2;
if(connected(temp1, temp2))//查找
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}