问题描述
小F面临一个有趣的挑战:给定一个数组,她需要将数组中的数字分为两组。分组的目标是使得一组数字的和的个位数等于给定的 A,另一组数字的和的个位数等于给定的 B。除此之外,还有一种特殊情况允许其中一组为空,但剩余数字和的个位数必须等于 A 或 B。小F需要计算所有可能的划分方式。
例如,对于数组 [1, 1, 1] 和目标 A = 1,B = 2,可行的划分包括三种:每个 1 单独作为一组,其余两个 1 形成另一组。如果 A = 3,B = 5,当所有数字加和的个位数为 3 或 5 时,可以有一组为非空,另一组为空。
测试样例
样例1:
输入:
n = 3,A = 1,B = 2,array_a = [1, 1, 1]输出:3
样例2:
输入:
n = 3,A = 3,B = 5,array_a = [1, 1, 1]输出:1
样例3:
输入:
n = 2,A = 1,B = 1,array_a = [1, 1]输出:2
样例4:
输入:
n = 5,A = 3,B = 7,array_a = [2, 3, 5, 7, 9]输出:0
解题思路
-
计算数组所有元素之和并进行初步判断(快速判断整体数组情况)
-
首先,通过一个
for-each循环遍历输入的整数数组array_a,将数组中的每个元素累加起来,得到整个数组所有元素的总和totalSum。代码如下:
-
int totalSum = 0;
for (int num : array_a) {
totalSum += num;
}
-
接着,检查这个总和
totalSum的个位数是否等于给定的整数A或者B。如果满足这个条件,那就意味着整个数组本身作为一种特殊的 “子集划分”(可以理解为把所有元素看作一组,另一组为空)是满足题目要求的。此时,将用于计数满足条件子集数量的变量count加 1,然后直接返回count对10000007取模后的结果,通过这样做可以跳过后续复杂的遍历所有子集并检查的操作,因为已经找到了一种符合条件的情况了。相关代码如下:
if ((totalSum % 10 == A) || (totalSum % 10 == B)) {
count++;
return count % 10000007;
}
-
通过位运算遍历数组的所有子集(核心操作,全面检查子集情况)
-
使用位运算来实现对数组
array_a所有子集的遍历。外层通过一个for循环,让变量mask从0开始,每次递增1,直到mask小于(1 << n)(这里的n是数组array_a的长度,1 << n相当于2的n次方,通过这种方式可以生成所有可能的子集表示形式)。每一个mask值都对应着一种数组元素的选取情况,也就是代表了一个特定的子集。代码如下:
-
for (int mask = 0; mask < (1 << n); mask++) {
-
对于每一个
mask所代表的子集选取情况,需要分别计算两组数字之和(相当于把这个子集划分成了两组)。为此,在内层再嵌套一个for循环,循环变量i从0到n - 1,用于遍历数组的每一个索引位置。在每次循环中,通过判断(mask & (1 << i))!= 0这个条件来确定数组array_a中对应位置的元素应该累加到哪一组数字和中。如果该条件成立,就把array_a[i]累加到sumA中;否则,累加到sumB中。这样就分别计算出了按照当前mask划分出来的两组数字的和sumA和sumB。代码如下:
int sumA = 0;
int sumB = 0;
for (int i = 0; i < n; i++) {
if ((mask & (1 << i))!= 0) {
sumA += array_a[i];
} else {
sumB += array_a[i];
}
}
-
计算好
sumA和sumB之后,需要检查它们是否满足题目所要求的特定条件。条件包含以下几种情况:- 常规情况:
sumA的个位数等于A且sumB的个位数等于B,这表示按照当前的子集划分方式,两组数字之和的个位数正好符合题目给定的A和B的要求,是一种满足条件的有效划分。 - 特殊情况一:
sumA等于0且sumB的个位数等于A,这相当于划分出来的其中一组数字为空(这里就是sumA对应的组为空),而另一组数字和的个位数满足题目要求(等于A)。 - 特殊情况二:
sumB等于0且sumA的个位数等于B,与特殊情况一类似,只不过此时是sumB对应的组为空,sumA对应的组数字和的个位数等于B。
如果满足上述任何一种条件,就将计数变量count加1,表示找到了一个满足要求的子集划分方式。代码如下:
- 常规情况:
if ((sumA % 10 == A && sumB % 10 == B) || (sumA == 0 && sumB % 10 == A) || (sumB == 0 && sumA % 10 == B)) {
count++;
}
- 返回最终结果并取模(确保结果符合要求)
在遍历完数组array_a的所有子集之后,将计数变量count对10000007取模,然后返回取模后的结果。这样做一方面是为了将结果控制在一个规定的范围内,避免因满足条件的子集数量过多导致数值过大而出现数据溢出等问题;另一方面也是遵循题目中设定的返回结果要取模的要求,确保返回的结果格式和数值范围符合预期。代码如下:
return count % 10000007;
解题代码
public class Main {
public static int solution(int n, int A, int B, int[] array_a) {
int count = 0;
// 先计算数组所有元素之和
int totalSum = 0;
for (int num : array_a) {
totalSum += num;
}
// 检查数组所有元素之和的个位数是否满足条件
if ((totalSum % 10 == A) || (totalSum % 10 == B)) {
count++;
// 如果满足,直接返回结果并取模,跳过后续子集遍历等操作
return count % 10000007;
}
// 遍历数组的所有子集
for (int mask = 0; mask < (1 << n); mask++) {
int sumA = 0;
int sumB = 0;
// 计算每个子集对应的两组数字之和
for (int i = 0; i < n; i++) {
if ((mask & (1 << i))!= 0) {
sumA += array_a[i];
} else {
sumB += array_a[i];
}
}
// 检查和的个位数是否符合要求
if ((sumA % 10 == A && sumB % 10 == B) || (sumA == 0 && sumB % 10 == A) || (sumB == 0 && sumA % 10 == B)) {
count++;
}
}
// 返回结果并取模
return count % 10000007;
}
public static void main(String[] args) {
int[] array1 = {1, 1, 1};
int[] array2 = {1, 1, 1};
int[] array3 = {1, 1};
System.out.println(solution(3, 1, 2, array1) == 3);
System.out.println(solution(3, 3, 5, array2) == 1);
System.out.println(solution(2, 1, 1, array3) == 2);
}
}
总结
AI刷题对于较为复杂的逻辑问题,像涉及位运算处理子集划分及多个条件判断的这类情况,AI 有着多方面的作用:它可辅助分析代码逻辑的正确性,清晰梳理各执行步骤的目的与流程,快速定位可能存在的逻辑漏洞或不合理之处,比如在判断子集划分后两组数字和个位数是否满足条件的复杂判断部分,能确认涵盖情况是否全面及有无多余错误判断;能基于现有代码实现,依据常见算法优化策略与编程最佳实践给出改进建议,像在计算数组总和、遍历子集时提供更高效数据结构或算法以降低时间复杂度及相应修改方案;有助于其他开发人员或后续维护者快速理解代码意图,通过自然语言解读整体功能、关键步骤作用等,方便代码复用、扩展与修改;还能依据代码功能及输入输出要求,生成更多不同边界情况、复杂场景下的测试用例,更全面地测试代码正确性与稳定性,减少潜在 Bug。