学习心得:第六届字节跳动青训营第一课 | 豆包MarsCode AI 刷题
一、题目解析
本次题目要求将一个整数数组分为两组,每组的和的个位数分别等于给定的两个数A和B。同时,题目允许其中一个分组为空,只要另一个分组的和的个位数等于A或B即可。这是一道典型的动态规划问题,通过构建多维数组来记录不同状态下的解,从而避免了暴力搜索带来的效率低下问题。
二、解题思路
-
状态定义:使用
dp[i][j][k]表示前i个数字中,第一组和的个位数为j,第二组和的个位数为k的方案数量。这里i从0到n(数组长度),j和k从0到9,因为只关心个位数。 -
状态转移:对于每一个数字,可以选择放入第一组或第二组。如果选择放入第一组,则上一个状态的
j值需要减去当前数字的个位数后对10取模;如果选择放入第二组,则k值做同样的处理。这样可以通过前一个状态推导出当前状态的值。 -
初始化:由于没有任何数字时,两组的和都为0,所以
dp[0][0][0]初始化为1。 -
结果计算:最后的结果需要考虑两种情况:
- 两组和的个位数分别为A和B的情况,即
dp[n][A][B]。 - 允许一组为空,而另一组和的个位数为A或B的情况。这里需要额外判断总和的个位数是否满足条件,并将其计入最终结果。
- 两组和的个位数分别为A和B的情况,即
三、代码实现
代码中定义了一个三维数组dp用于存储状态信息,通过遍历每个数字并更新状态来计算所有可能的划分方式。特别地,对于特殊情况的处理(即允许一组为空),在计算最终结果时进行了额外的判断和累加。
`
public class Main {
public static int solution(int n, int A, int B, int[] array_a) {
//dp: dp[i][j][k] 表示前i个数字,个位数分别为j和k时的划分方案数
int[][][] dp = new int[n+1][10][10];
//初始化dp数组
dp[0][0][0] = 1;
int sum = 0;
//第i个数
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];
}
//总和个位数是否为A
int a = sum % 10 == A? 1:0;
//总和个位数是否为B
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);
}
}`
四、测试与验证
在main方法中,作者提供了几个测试用例来验证算法的正确性。这些测试用例覆盖了不同的输入场景,确保了算法能够正确处理各种边界条件。
五、总结
本题通过动态规划的方法有效地解决了问题,避免了暴力求解带来的性能瓶颈。理解状态的定义、状态转移方程的设计以及如何初始化和计算最终结果,是解决这类问题的关键。此外,注意特殊情况的处理也是保证算法完整性的必要步骤。通过本题的学习,不仅加深了对动态规划的理解,也提高了处理复杂问题的能力。