数字分组求偶数和 | 豆包MarsCode AI刷题

79 阅读4分钟

问题分析

我们需要从多个数字组中选择一个数字,组成的新数的各位数字之和必须是偶数。为了有效地统计所有可能的组合,采用动态规划的方法。


解题思路

1. 分析奇偶性质

  • 一个数的各位数字之和为偶数,当且仅当:

    • 选择的奇数个数为偶数。
  • 每组数字可以分为 偶数奇数

    • 偶数:数字能被 2 整除(如 0, 2, 4, 6, 8)。
    • 奇数:数字不能被 2 整除(如 1, 3, 5, 7, 9)。

2. 动态规划定义

  • dp[i][0]:前 i 个数字组中,选择的组合使得当前奇数个数为偶数的总数。
  • dp[i][1]:前 i 个数字组中,选择的组合使得当前奇数个数为奇数的总数。

3. 状态转移

对于第 i 个数字组:

  • 假设该组中有 even_count 个偶数,odd_count 个奇数。

  • 更新 dp[i][0]dp[i][1]

    • dp[i][0]:当前奇数个数为偶数,可以由以下两种情况得到:

      1. i-1 组中奇数个数为偶数,再从第 i 组选择一个偶数: dp[i−1][0]×even_countdp[i-1][0] \times even_count
      2. i-1 组中奇数个数为奇数,再从第 i 组选择一个奇数: dp[i−1][1]×odd_countdp[i-1][1] \times odd_count

      dp[i][0]=dp[i−1][0]×even_count+dp[i−1][1]×odd_countdp[i][0] = dp[i-1][0] \times even_count + dp[i-1][1] \times odd_count

    • dp[i][1]:当前奇数个数为奇数:

      1. i-1 组中奇数个数为偶数,再从第 i 组选择一个奇数: dp[i−1][0]×odd_countdp[i-1][0] \times odd_count
      2. i-1 组中奇数个数为奇数,再从第 i 组选择一个偶数: dp[i−1][1]×even_countdp[i-1][1] \times even_count

      dp[i][1]=dp[i−1][0]×odd_count+dp[i−1][1]×even_countdp[i][1] = dp[i-1][0] \times odd_count + dp[i-1][1] \times even_count

4. 初始化

  • dp[0][0] = 1:初始状态下,奇数个数为偶数的组合数为 1(空组合)。
  • dp[0][1] = 0:初始状态下,奇数个数为奇数的组合数为 0。

5. 结果

  • 最终答案是 dp[n][0],表示前 n 个数字组中,使得奇数个数为偶数的组合数。

实现代码

以下是完整的 Python 实现:

def solution(numbers):
    n = len(numbers)
    # dp[i][0]: 前 i 组中奇数个数为偶数的组合数
    # dp[i][1]: 前 i 组中奇数个数为奇数的组合数
    dp = [[0, 0] for _ in range(n + 1)]
    
    # 初始化
    dp[0][0] = 1  # 初始奇数个数为偶数
    dp[0][1] = 0  # 初始奇数个数为奇数

    for i in range(1, n + 1):
        even_count = sum(1 for digit in str(numbers[i - 1]) if int(digit) % 2 == 0)
        odd_count = sum(1 for digit in str(numbers[i - 1]) if int(digit) % 2 != 0)
        
        # 状态转移
        dp[i][0] = dp[i - 1][0] * even_count + dp[i - 1][1] * odd_count
        dp[i][1] = dp[i - 1][0] * odd_count + dp[i - 1][1] * even_count

    # 返回奇数个数为偶数的组合数
    return dp[n][0]

if __name__ == "__main__":
    print(solution([123, 456, 789]) == 14)  # 示例 1
    print(solution([123456789]) == 4)      # 示例 2
    print(solution([14329, 7568]) == 10)   # 示例 3

测试样例分析

示例 1

输入

numbers = [123, 456, 789]

分析

  • 第 1 组:偶数 = {2},奇数 = {1, 3},偶数个数 = 1,奇数个数 = 2。
  • 第 2 组:偶数 = {4, 6},奇数 = {5},偶数个数 = 2,奇数个数 = 1。
  • 第 3 组:偶数 = {8},奇数 = {7, 9},偶数个数 = 1,奇数个数 = 2。
  • 有效组合数 = 14。

输出

14

示例 2

输入

numbers = [123456789]

分析

  • 唯一一组,偶数个数 = 4,奇数个数 = 5。
  • 有效组合数 = 4。

输出

4

示例 3

输入

numbers = [14329, 7568]

分析

  • 第 1 组:偶数 = {4, 2},奇数 = {1, 3, 9}。
  • 第 2 组:偶数 = {6, 8},奇数 = {7, 5}。
  • 有效组合数 = 10。

输出

10

时间复杂度

  1. 统计奇偶数:每组数字需要 O(k)O(k) 的时间,kk 是数字长度。
  2. 动态规划:共 nn 组,每组计算转移时是常数时间。
  3. 总复杂度:O(n⋅k)O(n \cdot k)。

空间复杂度

  • 动态规划表 O(n)O(n)。

总结

  • 本算法高效解决了从多组数字中选数,使得总和为偶数的组合计数问题。
  • 通过动态规划,避免了直接枚举所有组合的暴力方法,提升了性能。