问题分析
我们需要从多个数字组中选择一个数字,组成的新数的各位数字之和必须是偶数。为了有效地统计所有可能的组合,采用动态规划的方法。
解题思路
1. 分析奇偶性质
-
一个数的各位数字之和为偶数,当且仅当:
- 选择的奇数个数为偶数。
-
每组数字可以分为 偶数 和 奇数:
- 偶数:数字能被 2 整除(如
0, 2, 4, 6, 8)。 - 奇数:数字不能被 2 整除(如
1, 3, 5, 7, 9)。
- 偶数:数字能被 2 整除(如
2. 动态规划定义
dp[i][0]:前i个数字组中,选择的组合使得当前奇数个数为偶数的总数。dp[i][1]:前i个数字组中,选择的组合使得当前奇数个数为奇数的总数。
3. 状态转移
对于第 i 个数字组:
-
假设该组中有
even_count个偶数,odd_count个奇数。 -
更新
dp[i][0]和dp[i][1]:-
dp[i][0]:当前奇数个数为偶数,可以由以下两种情况得到:- 前
i-1组中奇数个数为偶数,再从第i组选择一个偶数: dp[i−1][0]×even_countdp[i-1][0] \times even_count - 前
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]:当前奇数个数为奇数:- 前
i-1组中奇数个数为偶数,再从第i组选择一个奇数: dp[i−1][0]×odd_countdp[i-1][0] \times odd_count - 前
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
时间复杂度
- 统计奇偶数:每组数字需要 O(k)O(k) 的时间,kk 是数字长度。
- 动态规划:共 nn 组,每组计算转移时是常数时间。
- 总复杂度:O(n⋅k)O(n \cdot k)。
空间复杂度
- 动态规划表 O(n)O(n)。
总结
- 本算法高效解决了从多组数字中选数,使得总和为偶数的组合计数问题。
- 通过动态规划,避免了直接枚举所有组合的暴力方法,提升了性能。