每日一题,并查集+暴力yyds!
题目链接:1627. 带阈值的图连通性 - 力扣(LeetCode)
题目信息:
有 n 座城市,编号从 1 到 n 。编号为 x 和 y 的两座城市直接连通的前提是: x 和 y 的公因数中,至少有一个 严格大于 某个阈值 threshold 。更正式地说,如果存在整数 z ,且满足以下所有条件,则编号 x 和 y 的城市之间有一条道路:
1.x % z == 0
2.y % z == 0
3.z > threshold
给你两个整数 n 和 threshold ,以及一个待查询数组,请你判断每个查询 queries[i] = [ai, bi] 指向的城市 ai 和 bi 是否连通(即,它们之间是否存在一条路径)。
返回数组 answer ,其中answer.length == queries.length 。如果第 i 个查询中指向的城市 ai 和 bi 连通,则 answer[i] 为 true ;如果不连通,则 answer[i] 为 false 。



题目解析:
给定n个城市,城市代码分别为1、2、3...n;若两城市间代码符合一个条件【城市代码的最大公因数大于threshold】则能连通,城市间可通过另一个城市进行连通。再给定一个城市代码数组queries(其中两两一组),返回一个List<Boolean>数组(其中的值为queries中一组城市之间是否连通)
解题方法:并查集+(暴力||手动筛选)
代码及超详细解析:
方法一:暴力yyds:

class Solution {
static int[]fa;//定义一个数组存储所有城市代码的根城市代码,第i个fa[i]表示第i个城市与第fa[i]连通
public List<Boolean> areConnected(int n, int threshold, int[][] queries) {
fa=new int[n+1];
for(int i=1;i<=n;i++)fa[i]=i;//初始化所有城市代码,使其根城市代码最开始指向自身
List<Boolean> answer=new ArrayList<>();//创建返回的List<boolean>
//此处与方法二不同,暴力太香了
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j+=i)
{
if(getCommonFactor(i,j)>threshold)merge(i,j);
}
}
//
//判断给定城市代码数组中一组城市是否连通
for(int[]q:queries)//使用增强for
{
if(find(q[0])==find(q[1]))answer.add(true);//两城市代码的根城市代码是否相同
else answer.add(false);
}
return answer;
}
//查找x城市代码的根城市代码
public int find(int x)
{
if(fa[x]==x)return x;//如果是他本身则返回自身
fa[x]=find(fa[x]);//压缩路径:find目的是查找根城市,将根城市代码赋给x城市,表示x城市与根城市连通
return fa[x];//返回根城市代码,用于比较
}
//连通两个城市的根城市
public void merge(int i,int j)
{
fa[find(i)]=find(j);//将i城市的根城市的根代码改成j城市的根城市代码
}
//返回两数字最大公因数
public int getCommonFactor(int a,int b)
{
int c=a%b;
while(c!=0)
{
a=b;
b=c;
c=a%b;
}
return b;
}
//
}
方法二:手动过滤不符合条件的城市代码,节省时间

class Solution {
static int[]fa;//定义一个数组存储所有城市代码的根城市代码,第i个fa[i]表示第i个城市与第fa[i]连通
public List<Boolean> areConnected(int n, int threshold, int[][] queries) {
fa=new int[n+1];
for(int i=1;i<=n;i++)fa[i]=i;//初始化所有城市代码,使其根城市代码最开始指向自身
List<Boolean> answer=new ArrayList<>();//创建返回的List<boolean>
//【此处分析忽略threshold=0的情况】人工过滤不符合条件的城市代码,从而节省时间
//i不从0、1开始,j不从0、1、i、i~2i-1开始。
//i的初始值为threshold+1,因为小于他的城市代码不跟任何城市连通
//j若从2i之前开始,(i,j)最大公因数为1,不符合threshold>1的情况
//j增量为i:先找出与i直接连通的城市,若两城市间接连通则两个城市根城市代码一定相同
for(int i=threshold+1;i<=n;i++)
{
for(int j=i*i;j<=n;j+=i)
merge(i,j);/连通两个城市的根城市代码
}
//判断给定城市代码数组中一组城市是否连通
for(int[]q:queries)//使用增强for
{
if(find(q[0])==find(q[1]))answer.add(true);//两城市代码的根城市代码是否相同
else answer.add(false);
}
return answer;
}
//查找x城市代码的根城市代码
public int find(int x)
{
if(fa[x]==x)return x;//如果是他本身则返回自身
fa[x]=find(fa[x]);//压缩路径:find目的是查找根城市,将根城市代码赋给x城市,表示x城市与根城市连通
return fa[x];//返回根城市代码,用于比较
}
//连通两个城市的根城市
public void merge(int i,int j)
{
fa[find(i)]=find(j);//将i城市的根城市的根代码改成j城市的根城市代码
}
}