题解:计算各位数字之和为偶数的方案数
题目描述
给定一组数字(从1到9组成的数字串),这些数字被分成多个小组。从每个小组中选择一个数字,组成一个新的数,目标是使得这个新数的各位数字之和为偶数。需要计算有多少种分组和选择方法可以满足这一条件。
解题思路
我们可以利用 动态规划 解决这个问题。核心思路是通过动态转移的方式,统计从每个数字组中选择数字后,数字之和为偶数或奇数的所有可能方案数。
关键点解析
-
奇偶性判断:
- 一个数的各位数字之和为偶数,当且仅当其数字之和对2取模为0。
- 从每个小组中选择一个数字时,可以记录奇数和偶数的数量,来更新当前选择的奇偶性。
-
动态规划:
- 用
dp[0]表示当前选择的数字之和为偶数的方案数,dp[1]表示当前选择的数字之和为奇数的方案数。 - 初始状态:
dp = [1, 0],即还未选择任何数字时,数字之和为偶数的方案数为1(空集),数字之和为奇数的方案数为0。
- 用
-
状态转移:
- 对于每个数字组:
- 如果选择一个偶数,和的奇偶性保持不变。
- 如果选择一个奇数,和的奇偶性会翻转。
- 假设当前数字组中有
even_count个偶数,odd_count个奇数,状态转移方程为:dp_new[0] = dp[0] * even_count + dp[1] * odd_countdp_new[1] = dp[1] * even_count + dp[0] * odd_count
- 更新后,将
dp_new的值赋给dp,继续处理下一个数字组。
- 对于每个数字组:
-
结果输出:
- 最终返回
dp[0],即所有方案中使得数字之和为偶数的数量。
- 最终返回
算法实现
以下是完整代码:
def solution(numbers):
# dp[0] 表示偶数和,dp[1] 表示奇数和
dp = [1, 0] # 初始状态,选择的数字和为0,显然是偶数
for group in numbers:
# group 是整数,先转换为字符串,再迭代每个字符计算奇偶数
group_str = str(group)
odd_count = sum(1 for digit in group_str if int(digit) % 2 == 1)
even_count = len(group_str) - odd_count
# 更新 dp 数组
dp_new = [0, 0]
dp_new[0] = dp[0] * even_count + dp[1] * odd_count
dp_new[1] = dp[1] * even_count + dp[0] * odd_count
dp = dp_new
return dp[0] # 返回偶数和的数量
if __name__ == "__main__":
# 测试用例
print(solution([123, 456, 789]) == 14)
print(solution([123456789]) == 4)
print(solution([14329, 7568]) == 10)
测试用例分析
-
测试用例1:
- 输入:
[123, 456, 789] - 分析:每组分别有奇数偶数可以选择,动态规划累计的偶数方案数为
14。 - 输出:
14
- 输入:
-
测试用例2:
- 输入:
[123456789] - 分析:只有一个数字组,共有4种选择可以使各位数字之和为偶数。
- 输出:
4
- 输入:
-
测试用例3:
- 输入:
[14329, 7568] - 分析:第一个组中奇偶数分别为
3和2,第二个组分别为2和2。最终符合条件的偶数和方案数为10。 - 输出:
10
- 输入:
时间复杂度分析
- 假设数字组的数量为
m,每个组中数字的平均长度为n:- 遍历所有数字组计算奇偶性,复杂度为 。
- 动态规划的转移操作为 。
- 因此,总时间复杂度为 。
空间复杂度分析
- 使用了固定大小的
dp数组,空间复杂度为 。
总结
本题通过动态规划解决了跨组数字奇偶性累计的复杂问题,核心在于使用 dp 表示当前奇偶性方案数,设计合理的状态转移方程。代码简洁高效,适用于不同大小的输入。
Java 代码
public class Solution {
public static int countEvenSumNumbers(int[] numbers) {
// dp[0] 表示偶数和方案数,dp[1] 表示奇数和方案数
int[] dp = {1, 0}; // 初始状态
for (int group : numbers) {
// 将当前数字组转换为字符串
String groupStr = String.valueOf(group);
int oddCount = 0;
int evenCount = 0;
// 统计奇数和偶数的数量
for (char digit : groupStr.toCharArray()) {
if ((digit - '0') % 2 == 1) { // 判断是否为奇数
oddCount++;
} else { // 偶数
evenCount++;
}
}
// 更新 dp 数组
int[] dpNew = new int[2];
dpNew[0] = dp[0] * evenCount + dp[1] * oddCount; // 和为偶数的方案
dpNew[1] = dp[1] * evenCount + dp[0] * oddCount; // 和为奇数的方案
dp = dpNew;
}
return dp[0]; // 返回最终和为偶数的方案数
}
public static void main(String[] args) {
// 测试用例
System.out.println(countEvenSumNumbers(new int[]{123, 456, 789}) == 14);
System.out.println(countEvenSumNumbers(new int[]{123456789}) == 4);
System.out.println(countEvenSumNumbers(new int[]{14329, 7568}) == 10);
}
}