一、题目解析:使数字和为偶数的组合计数
题目描述
给定若干数字组,从每个数字组中选择一个数字,组成一个新的数字。要求新数字的所有位数字之和为偶数,计算满足条件的所有组合数量。
题目分析
-
问题本质
- 偶数判定规则:一个数的数字和为偶数,当且仅当参与加和的数字中,奇数的个数是偶数。
- 因此,问题可以转化为:在每个数字组中选择一个数字,确保组合后奇数的个数为偶数。
-
核心挑战
- 从多个数字组中分别选择数字,选择的数字会影响最终的奇偶性。
- 对所有组合逐一检查虽然可行,但时间复杂度高;因此,需要利用算法优化。
算法思路
-
状态转移法
将问题拆解为逐步选择的子问题,利用当前选择的数字,更新“奇数个数”的状态。可以通过递归或动态规划实现。 -
递归回溯法
- 递归处理数字组,逐步选择数字。
- 利用递归的层级管理不同数字组的选择,传递当前奇偶状态(奇数个数)。
- 在递归出口处,判断奇数个数是否为偶数,如果满足条件,则计数。
-
关键优化
- 不需要存储所有组合,只需累积当前状态,降低空间复杂度。
- 可提前剪枝:当某路径下的奇数个数已不可能为偶数时,提前终止递归。
二、代码实现与详解
C++ 实现
#include <iostream>
#include <vector>
#include <string>
#include <functional>
// 计算满足条件的组合数量
int countEvenSums(const std::vector<std::string>& numbers) {
int count = 0; // 符合条件的组合数
// 辅助函数:递归选择数字组中的元素并判断奇偶性
std::function<void(int, int)> dfs = [&](int index, int oddCount) {
// 递归出口:当遍历完所有数字组时,检查奇数个数是否为偶数
if (index == numbers.size()) {
if (oddCount % 2 == 0) {
count++;
}
return;
}
// 遍历当前数字组的每个数字
for (char digit : numbers[index]) {
int isOdd = (digit - '0') % 2; // 判断当前数字是否为奇数
dfs(index + 1, oddCount + isOdd); // 更新奇数个数并递归下一组
}
};
dfs(0, 0); // 从第0组开始,奇数个数初始为0
return count;
}
// 测试用例
int main() {
std::vector<std::string> numbers1 = {"123", "456", "789"}; // 示例输入1
std::cout << countEvenSums(numbers1) << std::endl; // 输出: 14
std::vector<std::string> numbers2 = {"123456789"}; // 示例输入2
std::cout << countEvenSums(numbers2) << std::endl; // 输出: 4
std::vector<std::string> numbers3 = {"14329", "7568"}; // 示例输入3
std::cout << countEvenSums(numbers3) << std::endl; // 输出: 10
return 0;
}
代码详解
-
递归设计
- 输入:当前处理的数字组索引
index和当前奇数个数oddCount。 - 递归逻辑:对于当前数字组的每个数字,更新奇数计数,并递归到下一组。
- 递归出口:当遍历完所有数字组时,检查
oddCount是否为偶数。
- 输入:当前处理的数字组索引
-
时间复杂度
- 遍历所有组合:若数字组数量为 N,每组平均长度为 M,则时间复杂度为 O(N×M)O(N \times M)O(N×M)。
-
空间复杂度
- 未存储所有组合,递归栈的深度为数字组的数量 NNN,因此空间复杂度为 O(N)O(N)O(N)。
三、知识总结:递归与问题分解的应用
递归核心要点
- 状态管理:递归时,需传递当前的状态,例如奇数个数。
- 出口条件:设计清晰的递归结束条件,避免无限递归。
- 优化递归:可以通过剪枝减少无效路径的计算。
递归常见场景
- 组合问题:如本题、全排列、子集生成等。
- 动态规划:递归通过记忆化实现优化。
- 树形结构遍历:如二叉树的深度优先遍历。
四、学习计划:如何提升递归能力
分步掌握递归
- 从简单问题入手,例如计算斐波那契数列或阶乘。
- 学习结合状态转移的递归,如背包问题。
- 挑战更复杂的题目,例如全排列、括号生成等。
工具支持
- 使用豆包MarsCode的代码调试功能,跟踪递归状态。
- 配合书籍如《算法图解》深入学习。
五、豆包MarsCode AI 帮助刷题的亮点
- 智能推荐题目:根据个人学习进度推送适合的练习。
- 错题解析:精准分析错因,给出优化建议。
- 代码提示:在卡住时提供实现思路和关键代码段。
六、总结
通过递归解决“数字和为偶数的组合计数”问题,不仅加深了对递归核心思想的理解,也提高了问题分解和状态管理的能力。未来将结合豆包MarsCode AI 的强大功能与经典资源,持续探索更深层次的算法问题,期待与你们分享更多学习成果!