问题描述
小M拿到一个数组,她可以进行多次操作,每次操作可以选择两个元素 aiai 和 ajaj,并选择 aiai 的一个因子 xx,然后将 aiai 变为 ai/xai/x,并将 ajaj 变为 aj×xaj×x。她的目标是通过有限次操作,使得数组中的每个元素最多只包含一种素因子。
素因子的定义是:若 xx 能被素数 pp 整除,那么 pp 是 xx 的一个素因子。例如,1212 的素因子有 22 和 33。
你的任务是判断是否有可能通过有限次操作,使数组中的每个元素最多只包含一种素因子。如果可以,输出 "Yes",否则输出 "No"。
测试样例
样例1:
输入:
n = 4 ,a = [1, 2, 3, 4]
输出:'Yes'
样例2:
输入:
n = 2 ,a = [10, 12]
输出:'No'
样例3:
输入:
n = 3 ,a = [6, 9, 15]
输出:'Yes'
问题分析
我们的问题是判断是否能通过特定操作,将数组中每个元素转化为最多只包含一种素因子的形式。每个元素的素因子可以通过其质因数分解获得。需要解决的核心点包括:
- 如何获取每个数字的素因子。
- 如何判断数组中两个元素是否可以通过归并操作满足条件。
算法思路
思路概述
- 素因子分解:通过质因数分解,将每个数字分解为素数因子的集合。
- 直接判断单因子情况:如果数组中的每个数字本身已经最多只包含一种素因子,则直接输出
Yes。 - 尝试归并素因子:遍历数组的任意两个数字,检查它们的素因子集合是否可以合并成最多只包含两种素因子的集合。如果可以,则满足题目条件。
- 最终判断:如果无法找到满足条件的操作组合,则输出
No。
详细步骤
-
素因子获取:
- 对每个数组元素进行质因数分解。
- 使用 筛选法提取素因子集合。
- 特别处理:对于 n=1n = 1n=1,它没有素因子。
-
单元素检查:
- 遍历数组,检查每个元素的素因子集合的大小,如果都 ≤1 \leq 1≤1,直接返回
Yes。
- 遍历数组,检查每个元素的素因子集合的大小,如果都 ≤1 \leq 1≤1,直接返回
-
尝试合并素因子:
- 遍历数组中任意两个元素的素因子集合。
- 合并这两个集合,若结果集合的大小 ≤2 \leq 2≤2,则返回
Yes。
-
特殊情况处理:
- 如果所有数字的素因子集合中无法找到满足条件的组合,则返回
No。
- 如果所有数字的素因子集合中无法找到满足条件的组合,则返回
算法介绍
以下是代码的逻辑逐步解析:
1. 获取素因子函数 getPrimeFactors
private static Set<Integer> getPrimeFactors(int n) {
Set<Integer> factors = new HashSet<>();
// 处理偶数因子2
while (n % 2 == 0) {
factors.add(2);
n /= 2;
}
// 处理奇数因子
for (int i = 3; i * i <= n; i += 2) {
while (n % i == 0) {
factors.add(i);
n /= i;
}
}
// 如果最后剩余大于2的数,它本身就是素数
if (n > 2) {
factors.add(n);
}
return factors;
}
功能:
- 利用辗转相除法,获取数字 nnn 的所有素因子。
- 将 nnn 从偶数开始分解,接着处理奇数部分。
- 如果剩余的数大于 222,直接视为素数加入因子集合。
2. 主逻辑 solution
java
复制代码
public static String solution(int n, int[] a) {
// 获取每个数的素因子
List<Set<Integer>> primeFactorSets = new ArrayList<>();
for (int num : a) {
primeFactorSets.add(getPrimeFactors(num));
}
// 如果每个元素已经只有一种或没有素因子,直接返回Yes
if (primeFactorSets.stream().allMatch(factors -> factors.size() <= 1)) {
return "Yes";
}
// 检查任意两个元素的素因子组合是否可以归并
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
Set<Integer> combinedFactors = new HashSet<>(primeFactorSets.get(i));
combinedFactors.addAll(primeFactorSets.get(j));
// 如果总素因子数不超过2,说明可以通过操作归并
if (combinedFactors.size() <= 2) {
return "Yes";
}
}
}
return "No";
}
主逻辑:
- 将数组中的每个元素分解为素因子集合
primeFactorSets。 - 判断是否所有集合的大小 ≤1,若是则直接返回
Yes。 - 对于数组中的任意两个元素,合并它们的素因子集合,判断合并后的集合大小是否 ≤2。
- 如果满足条件,输出
Yes;否则返回No。
算法复杂度
-
时间复杂度:
- 获取素因子:O(n⋅m)O(n \cdot \sqrt{m})O(n⋅m),其中 m 是数组中最大值。
- 两两组合检查:O(n2⋅k)O(n^2 \cdot k)O(n2⋅k),其中 k 是集合操作的开销。
总体复杂度:O(n⋅m+n2⋅k)O(n \cdot \sqrt{m} + n^2 \cdot k)O(n⋅m+n2⋅k)。
-
空间复杂度:O(n⋅k),存储素因子集合。
该算法的目标是判断一个数组能否通过特定操作,使每个元素最多只包含一种素因子。操作允许选择两个元素,将其中一个数字按其某个因子缩小,另一个数字按同样的因子放大。素因子的定义是一个数能被哪些素数整除,例如 12的素因子为 2,3。
算法首先通过质因数分解,获取数组中每个数字的素因子集合。如果所有数字本身已经满足最多只有一种素因子,则直接返回 "Yes"。否则,算法逐一检查任意两个元素的素因子集合,尝试合并,判断是否能通过有限次操作使所有元素符合要求。如果能找到满足条件的组合,则返回 "Yes",否则返回 "No"。
算法通过分解素因子和集合运算高效实现,解决了素因子操作与组合的问题,适用于数组规模和元素值较大的情况。
学习心得
通过这道题的学习,我对素因子分解和集合操作有了更加深入的理解。题目表面看似简单,但实际上涉及到数学性质和算法设计的结合。通过对素因子的分解和归并,我们需要找到解决问题的最优路径,尤其在判断数组元素的素因子是否可以通过特定操作归并时,充分运用了质因数分解和集合操作的思想。
在实现过程中,我认识到编程中的数学工具(如质因数分解)的重要性。这不仅是解决问题的基础,也是提升算法效率的关键。此外,利用集合来存储和合并因子集合,提高了代码的可读性和扩展性。通过两两组合的方式遍历素因子集合,可以直观地验证条件是否成立,进一步巩固了我对集合操作的掌握。
这道题还让我学会了如何将复杂的问题分解为多个可执行的步骤:从素因子的提取到条件判断,再到整体的逻辑串联,体现了算法设计中的结构化思维。最后,通过分析不同测试用例,我也意识到边界条件和特殊情况处理的重要性,比如 1 的素因子为空等,这些细节往往决定了程序的健壮性。
这次学习让我更加理解了算法与数学的紧密联系,也锻炼了自己分解复杂问题的能力,为之后处理类似问题奠定了基础。