问题描述
小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
Tips:数组里的数字可以相等,但每个数字都是独一无二的。
比如 array_a = [1, 1, 1],A = 1,B = 2,则一共有三组划分方式:
第一种: A:a[0] B:a[1]、a[2]
第二种: A:a[1] B:a[0]、a[2]
第三种: A:a[2] B:a[0]、a[1]
样例2:
输入:n = 3,A = 3,B = 5,array_a = [1, 1, 1]
输出:1
Tips:可以将所有数字都划分到同一组,使其和的个位数等于 A 或 B;另一组为空。
比如 array_a = [1, 1, 1],A = 3,B = 5,则共有一组划分方式:
A:a[0]、a[1]、a[2] B:空
样例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
解题思路
1.规范化数组:
将数组中的每个元素取模 10,确保所有元素都在 0 到 9 的范围内。这样做是因为我们只关心和的个位数。
2.计算总和的个位数:
计算整个数组的和的个位数 total_sum。如果这个和的个位数等于 A 或 B,那么我们可以直接返回 1,因为此时可以将一组设置为空,另一组的和即为 total_sum。
3.检查总和的条件:
如果 total_sum 不等于 (A + B) % 10,那么就没有可能的划分,因为两个组的和的个位数之和不可能等于总和的个位数。
4.动态规划:
1)使用动态规划的方法来计算可能的划分方式。我们定义一个二维数组 f[i][j],表示前 i 个数字中,能够组成和的个位数为 j 的方式数量。
2)初始化 f[0][0] = 1,表示使用 0 个数字可以得到和为 0 的一种方式。
3)对于每个数字,我们可以选择将其放入第一组或不放入第一组,从而更新状态。
5.最终结果:
最终的结果是 f[n][B],表示使用所有数字能够得到和的个位数为 B 的方式数量。
代码实现(CPP)
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int solution(int n, int A, int B, vector<int>& array_a) {
// 将元素规范化到 [0, 9] 的范围内
for (int& x : array_a) {
x %= 10;
}
int total_sum = accumulate(array_a.begin(), array_a.end(), 0) % 10;
// 检查总和是否符合要求
if (total_sum == A || total_sum == B) {
return 1;
}
if (total_sum != (A + B) % 10) {
return 0;
}
// 动态规划数组初始化
vector<vector<int>> f(n + 1, vector<int>(10, 0));
f[0][0] = 1;
// 动态规划更新
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < 10; ++j) {
f[i][j] += f[i - 1][j]; // 不将第 i 个数字放入第一组
f[i][j] += f[i - 1][(j - array_a[i - 1] + 10) % 10]; // 将第 i 个数字放入第一组
}
}
return f[n][B];
}
int main() {
// You can add more test cases here
std::vector<int> array1 = {1, 1, 1};
std::vector<int> array2 = {1, 1, 1};
std::vector<int> array3 = {1, 1};
std::cout << (solution(3, 1, 2, array1) == 3) << std::endl;
std::cout << (solution(3, 3, 5, array2) == 1) << std::endl;
std::cout << (solution(2, 1, 1, array3) == 2) << std::endl;
return 0;
}
复杂度分析
时间复杂度
动态规划的状态转移需要 O(n * 10),其中 n 是数组的大小,10 是个位数的范围。因此,整体的时间复杂度为 O(n)。
空间复杂度
我们使用了一个二维数组 f,其大小为 (n + 1) * 10,因此空间复杂度为 O(n)。