题目背景
若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任 意给出的两个人是否具有亲戚关系。
题目描述
规定: 和 是亲戚, 和 是亲戚,那么 和 也是亲戚。如果 , 是亲戚,那么 的亲戚都是
的亲戚, 的亲戚也都是 的亲戚。
输入格式
第一行:三个
,分别表示有
个人,
个亲戚关系,询问p对亲戚关系。
以下m行:每行两个数
,表示
和
具有亲戚关系。
接下来
行:每行两个数
询问
和
是否有亲戚关系。
输出格式
p 行,每行一个 Yes
或 No
。表示第 i 个询问的答案为“具有”或“不具有”亲戚关系。
输入 #1
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
输出 #1
Yes
Yes
No
解题思路
本题是一个经典的并查集问题,要判断x和y是否为亲戚只需要判断x和y是否属于同一家族集合,此处家族集合可以理解为族谱图:
由族谱图可以进一步看出,要判断x和y是否为亲戚只需要判断x和y的祖先是否为同一人即可。
以本题为例,首先初始化所有人的祖先是自己即pre[i] = i,若a与b为亲戚则将他们加入同一家族,并令b为a的祖先即pre[a] = b(这里可以随意指定并不影响最终的判断结果)。引入样例数据分析如下:
(1,2):1的祖先是1,2的祖先是2,令2为1的祖先即pre[1] = 2;
(1,5):1的祖先是2,5的祖先是5,令5为2的祖先即pre[2] = 5;
(3,4):3的祖先是3,4的祖先是4,令4为3的祖先即pre[3] = 4;
(5,2):5的祖先是5,2的祖先是5,祖先相同结构不变;
(1,3):1的祖先是5,3的祖先是4,令4为5的祖先即pre[5]=4。
根据最终的树形结构可以很容易的判断两人是否为亲戚,只需要判断两人的祖先是否为同一人即可。
完整代码:
import java.util.*;
public class Main {
static int n, m, p;
static int[] pre = new int[5001];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
p = sc.nextInt();
for (int i = 1; i <= n; i++) {
pre[i] = i;
}
for (int i = 0; i < m; i++) {
join(sc.nextInt(),sc.nextInt());
}
for (int i = 0; i < p; i++) {
if (find(sc.nextInt()) == find(sc.nextInt())) {
System.out.println("Yes");
} else{
System.out.println("No");
}
}
}
//寻找x的祖先
static int find(int x) {
if (pre[x] == x) return x;
return pre[x] = find(pre[x]);//这里使用了路径压缩
}
//判断两人的关系并将其加入族谱
static void join(int x, int y) {
int fx = find(x), fy = find(y);
if ( fx != fy) pre[fx] = fy;
}
}
核心方法为find()和join,其中find()采用了路径压缩,可以节省最终的判断时间。