题目解析:数字分组求偶数和|豆包MarsCodeAI刷题

212 阅读7分钟

问题描述

小M面对一组从 1 到 9 的数字,这些数字被分成多个小组,并从每个小组中选择一个数字组成一个新的数。目标是使得这个新数的各位数字之和为偶数。任务是计算出有多少种不同的分组和选择方法可以达到这一目标。

  • numbers: 一个由多个整数字符串组成的列表,每个字符串可以视为一个数字组。小M需要从每个数字组中选择一个数字。

例如对于[123, 456, 789],14个符合条件的数为:147 149 158 167 169 248 257 259 268 347 349 358 367 369


测试样例

样例1:

输入:numbers = [123, 456, 789]
输出:14

样例2:

输入:numbers = [123456789]
输出:4

样例3:

输入:numbers = [14329, 7568]
输出:10

  • 思路: 这个问题要求我们从每个数字组中选择一个数字,使得最终选择的数字之和为偶数。我们可以将问题分解为两个状态:当前选择的数字之和为偶数(记为even)和当前选择的数字之和为奇数(记为odd)。对于每个数字组,我们需要计算选择奇数和偶数后的新状态,并更新evenodd

图解: 我们可以画出一个状态转移图来表示这个过程。对于每个数字组,我们有两个选择:奇数或偶数。选择奇数会将even状态转变为odd状态,反之亦然。最终,我们需要的是even状态的数量,因为只有这样才能保证所有选择的数字之和为偶数。 `

public class Main {

public static int solution(int[] numbers) {
    int even = 1; // 选择偶数的组合
   int odd = 0; // 选择奇数的组合数

    for (int number : numbers) {
        int groupEvenCount = 0; // 当前组中偶数的数量
        int groupOddCount = 0; // 当前组中奇数的数量

        while (number > 0) {
            int digit = number % 10;
            if (digit % 2 == 0) {
                groupEvenCount++;
            } else {
                groupOddCount++;
            }
            number /= 10;
        }

        int newEven = even * groupEvenCount + odd * groupOddCount; // 新的偶数组合数
        int newOdd = even * groupOddCount + odd * groupEvenCount; // 新的奇数组合数

        even = newEven;
        odd = newOdd;
    }

    return even; // 返回偶数组合数
}
}

上述代码解决的问题是一个组合问题,而不是传统意义上的动态规划问题。代码的目的是计算从每个数字组中选择一个数字,使得这些数字的总和为偶数的不同选择方法的数量。这个问题可以通过动态规划的思想来解决,但实际上它更接近于组合数学中的计数问题。

算法原理:

  1. 状态定义

    • even:表示当前选择的数字之和为偶数的组合数。
    • odd:表示当前选择的数字之和为奇数的组合数。
  2. 状态转移

    • 对于每个数字组,我们需要决定是从该组中选择一个奇数还是一个偶数。

    • 如果我们选择一个偶数,那么:

      • 之前是even状态的,加上这个偶数后仍然是even状态。
      • 之前是odd状态的,加上这个偶数后会变成even状态。
    • 如果我们选择一个奇数,那么:

      • 之前是even状态的,加上这个奇数后会变成odd状态。
      • 之前是odd状态的,加上这个奇数后仍然是odd状态。
  3. 状态更新

    • 对于每个数字组,我们根据该组中奇数和偶数的数量,更新evenodd的状态。具体来说,我们将even乘以该组中偶数的数量,将odd乘以该组中奇数的数量,然后将这两个结果相加,得到新的even状态。同样,我们也计算新的odd状态。
  4. 边界条件

    • 初始时,even = 1odd = 0,因为我们还没有做任何选择,所以默认总和为偶数的组合数为1(即没有选择任何数字)。
  5. 最终结果

    • 最后,我们只关心总和为偶数的组合数,因此返回even的值。
    • 在这段代码中,我们遍历每个数字组,计算该组中奇数和偶数的数量,并根据这些数量更新evenodd状态。这个过程重复进行,直到所有数字组都被处理完毕。最后,我们返回even状态的值,即所有选择的和为偶数的组合数。这个算法的时间复杂度是O(n * m),其中n是数字组的数量,m是每个数字组中数字的最大位数。

知识总结

新知识点:

  1. 状态转移方程:在解决动态规划问题时,理解如何根据当前状态推导出下一个状态是关键。
  2. 奇偶性问题:理解奇偶性问题的性质,如何通过选择奇数或偶数来控制总和的奇偶性。
  3. 动态规划(Dynamic Programming,简称DP):是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中使用的,通过把原问题分解为相对简单的子问题的方式解决复杂问题的方法。

动态规划问题具有以下几个典型特征:

  1. 重叠子问题:问题的解决方案可以通过解决其子问题的解决方案来构建。这意味着在递归方法中,相同的子问题会被多次计算。
  2. 最优子结构:一个问题的最优解包含其子问题的最优解。也就是说,子问题的最优解可以用来构建原问题的最优解。
  3. 无后效性:一旦某个状态被确定,它就不受之后决策的影响。换句话说,未来的状态不依赖于过去的状态,而只依赖于当前的状态。

动态规划通常用于解决以下类型的问题:

  • 计数问题:如计算不同路径的数量、不同分割方式的数量等。
  • 最优化问题:如寻找最长递增子序列、最小编辑距离、最大子数组和等。
  • 资源分配问题:如背包问题、硬币找零问题等。

动态规划的解决方案通常涉及以下几个步骤:

  1. 定义状态:确定状态是什么,以及状态之间的关系。
  2. 建立状态转移方程:根据问题的特点,推导出从一个状态到另一个状态的转移方式。
  3. 确定边界条件:确定问题的基本情况,也就是递归的终止条件。
  4. 计算结果:根据状态转移方程和边界条件,计算出原问题的解。

动态规划的关键优势在于它能够通过记忆化(memoization)或表格化(tabulation)来避免重复计算相同的子问题,从而提高算法的效率。记忆化是指在递归过程中存储子问题的解,而表格化则是在迭代过程中使用表格来存储所有子问题的解。这两种方法都可以有效减少计算量,从指数时间复杂度降低到多项式时间复杂度。

理解和建议:

  • 理解:在解决这类问题时,关键是识别出状态转移的规律,并能够将问题分解为更小的子问题。
  • 建议:对于入门同学,建议从简单的动态规划问题开始,逐步理解状态转移的概念,并通过画图来帮助理解问题。

学习计划

制定刷题计划:

  1. 每日刷题:每天至少解决一到两个问题,保持连续性。
  2. 难度递增:从易到难,逐步增加题目的难度。
  3. 定期复习:每周回顾本周解决的问题,巩固知识点。

利用错题进行针对性学习:

  1. 记录错题:将做错的题目记录下来,分析错误原因。
  2. 针对性练习:针对错误原因,寻找类似的题目进行练习。

工具运用

结合AI刷题功能:

  1. 智能推荐:利用AI推荐系统,找到适合自己的题目。
  2. 错题分析:使用AI分析错题,找出薄弱环节。

与其他学习资源结合:

  1. 在线课程:结合在线课程,系统学习算法和数据结构。
  2. 书籍:阅读经典算法书籍,深入理解算法原理。

通过这样的学习方法和工具运用,可以更高效地利用豆包MarsCode AI刷题功能,提高学习效果。