学习心得:第六届字节跳动青训营第一课 | 豆包MarsCode AI 刷题

126 阅读3分钟

学习心得:第六届字节跳动青训营第一课 | 豆包MarsCode AI 刷题

一、题目解析

本次题目要求将一个整数数组分为两组,每组的和的个位数分别等于给定的两个数A和B。同时,题目允许其中一个分组为空,只要另一个分组的和的个位数等于A或B即可。这是一道典型的动态规划问题,通过构建多维数组来记录不同状态下的解,从而避免了暴力搜索带来的效率低下问题。

二、解题思路

  1. 状态定义:使用dp[i][j][k]表示前i个数字中,第一组和的个位数为j,第二组和的个位数为k的方案数量。这里i从0到n(数组长度),jk从0到9,因为只关心个位数。

  2. 状态转移:对于每一个数字,可以选择放入第一组或第二组。如果选择放入第一组,则上一个状态的j值需要减去当前数字的个位数后对10取模;如果选择放入第二组,则k值做同样的处理。这样可以通过前一个状态推导出当前状态的值。

  3. 初始化:由于没有任何数字时,两组的和都为0,所以dp[0][0][0]初始化为1。

  4. 结果计算:最后的结果需要考虑两种情况:

    • 两组和的个位数分别为A和B的情况,即dp[n][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方法中,作者提供了几个测试用例来验证算法的正确性。这些测试用例覆盖了不同的输入场景,确保了算法能够正确处理各种边界条件。

五、总结

本题通过动态规划的方法有效地解决了问题,避免了暴力求解带来的性能瓶颈。理解状态的定义、状态转移方程的设计以及如何初始化和计算最终结果,是解决这类问题的关键。此外,注意特殊情况的处理也是保证算法完整性的必要步骤。通过本题的学习,不仅加深了对动态规划的理解,也提高了处理复杂问题的能力。