二分数字组合
问题描述
小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
思路
利用动态规划,dp[i][j][k]表示在考虑前i个数字时,第一组的和的个位数为j,第二组的和的个位数为k的划分方案数。不要忘记特殊情况,即计算a和b,分别表示总和的个位数是否等于A和B。
dp转移方程的含义
对于每个数字array_a[i-1](注意数组索引从0开始,而这里用i表示当前考虑到的第i个数字,实际上在数组中是array_a[i-1]),有两种选择:
-
将该数字加入第一组:
此时,第一组的和的个位数会发生变化,而第二组的和的个位数保持不变。 新的第一组的和的个位数由
j = (x +array_a[i-1] % 10) % 10计算得到x =((j + 10) - array_a[i-1] % 10) % 10
注意:
array_a[i-1] % 10计算array_a[i-1]的个位数j + 10是为了确保在减去array_a[i-1]的个位数后,结果仍然是一个正数- 最后通过再次对 10 取模确保新的个位数在0 到 9 之间
-
将该数字加入第二组:
此时第二组的和的个位数会发生变化,而第一组的和的个位数保持不变。 新的第二组的和的个位数可以通过
((k + 10) - array_a[i-1] % 10) % 10计算得到
代码
public static int solution(int n, int A, int B, int[] array_a) {
int[][][] dp = new int[n+1][15][15];
dp[0][0][0] = 1;
int sum = 0;
for(int i = 1;i <= array_a.length;i++){
for(int j = 0;j < 10;j++){
for(int k = 0;k < 10;k++){
dp[i][j][k] += dp[i-1][((j + 10) - array_a[i-1] % 10) % 10][k];
dp[i][j][k] += dp[i-1][j][((k + 10) - array_a[i-1] % 10) % 10];
}
}
sum += array_a[i-1];
}
int a = sum % 10 == A? 1:0;
int b = sum % 10 == B? 1:0;
int result = dp[n][A][B] + a + b;
return result;
}
public static void main(String[] args) {
// You can add more test cases here
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);
}
}