Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
一、题目描述:
给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。x 和 y 是亲戚,y 和 z 是亲戚,那么 x 和 z 也是亲戚。如果 x,y 是亲戚,那么 x 的亲戚都是 y 的亲戚,y 的亲戚也都是 x 的亲戚。
来源:洛谷 www.luogu.com.cn/problem/P15…
输入格式
第一行:三个整数 n,m,p(n,m,p≤5000),分别表示有 n 个人,m 个亲戚关系,询问 p 对亲戚关系。
以下 m 行:每行两个数 Mi,Mj, 1≤Mi, Mj≤N,表示 Mi和Mj 具有亲戚关系。
接下来 p 行:每行两个数 Pi, Pj,询问 Pi 和 Pj 是否具有亲戚关系。
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
输出格式
p 行,每行一个 Yes 或 No。表示第 ii 个询问的答案为“具有”或“不具有”亲戚关系。
Yes
Yes
No
二、思路分析:
并查集即合并查询。我们这题的思路给每个人做一个标记,就是它的“祖先” ,如果这两个人的祖先是同一个人的话,那么它们就有亲戚关系。
首先n个人,每个人都是自己的祖先。 当x和y有亲戚关系,那就把y的祖先设置为x的祖先或者把x的祖先设置为y的祖先, 并且,也是最重要的部分利用递归在查找x或者y的祖先的时候,把它们的祖先都统一,终止条件就是当z的祖先是z自己的话,那它就是那一串的祖先,然后把z返回给上一层并赋值(这里赋值就是统一祖先)。
这就是上面那个例子的运算过程, 橙色的就是每次输入一组亲戚关系会关联到的地方(用的表格展示,动画太难了,我不会....)
三、AC 代码:
import java.util.Scanner;
public class Main {
// f 就是亲戚关系的数组
static int [] f ;
public static void main(String[] args) {
//有n个人,m个亲戚关系,询问p对亲戚关系。
Scanner sr = new Scanner(System.in);
int n = sr.nextInt() ;
int m = sr.nextInt() ;
int p = sr.nextInt() ;
f = new int [n+1];
// 赋初值,也就是上面所说的 每个人都是自己的祖先
for( int i = 1 ; i <= n ; i++ )
f[i] = i ;
for( int i = 0 ; i < m ; i++ ) {
// Mi和Mj具有亲戚关系
int Mi = sr.nextInt() ;
int Mj = sr.nextInt() ;
int xx = fun( Mi ) ; // 找Mi的祖宗
int yy = fun( Mj ) ; // 找Mj的祖宗
f[yy] = xx ; // 把xx和yy的祖宗统一
}
// Pi和Pj是否具有亲戚关系。
for( int i = 0 ; i < p ; i++ ) {
int Pi = sr.nextInt() ;
int Pj = sr.nextInt() ;
// 判断祖宗是否一样
if( fun(Pi) == fun(Pj) ) {
System.out.println("Yes");
}else {
System.out.println("No");
}
}
}
// 使用递归查询祖宗,统一祖宗
private static int fun(int x) {
// 递归的终止条件 - 找到了祖宗
if( f[x] == x )
return x ;
// 把x的祖宗赋值给f[x]
return f[x] = fun( f[x] ) ;
}
}
四、总结:
总的来说就是把两个有亲戚关系的人放到一个集合里面,这个集合的标记就是 “祖先”, 把输入数据分到不同或者相同的集合里面,重点在这个统一祖宗函数(fun)。完了就可以直接判断了