剪枝(Pruning) 是一种优化策略,用于在搜索过程中提前终止无效分支的探索,主要应用于:
原理:当前路径明显无法满足条件时终止搜索
// 示例:组合总和问题中提前终止无效路径
if (current_sum > target) return; // 不再继续搜索
原理:当当前路径已不可能优于已知最优解时终止
// 示例:旅行商问题中剪枝
if (current_cost >= best_cost) return; // 已有更优解
原理:记录已计算状态避免重复计算
// 示例:斐波那契数列计算
if (memo[n] != -1) return memo[n];
原理:消除重复对称情况
// 示例:N皇后问题中限制第一行的位置范围
if (row == 0 && col > N/2) return; // 利用对称性减少计算
给定候选数组candidates
和目标值target
,找出所有不重复的组合,使得组合中数字的和等于target
(每个数字可重复使用)
#include
#include
using namespace std;
void backtrack(vector<int>& candidates, int target,
int start, vector<int>& path,
vector<vector<int>>& result) {
if (target == 0) {
result.push_back(path);
return;
}
for (int i = start; i < candidates.size(); ++i) {
// 关键剪枝点:排序后提前终止
if (candidates[i] > target) break;
// 避免重复组合
if (i > start && candidates[i] == candidates[i-1]) continue;
path.push_back(candidates[i]);
backtrack(candidates, target - candidates[i], i, path, result);
path.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end()); // 预处理排序
vector<vector<int>> result;
vector<int> path;
backtrack(candidates, target, 0, path, result);
return result;
}
candidates
有序,方便后续剪枝candidates[i] > target
时,后续更大元素无需考虑void solveNQueens(int n, int row,
vector<int>& cols,
vector<vector<string>>& results) {
if (row == n) {
// 生成棋盘
return;
}
for (int col = 0; col < n; ++col) {
// 剪枝:检查冲突
bool valid = true;
for (int r = 0; r < row; ++r) {
if (cols[r] == col ||
abs(col - cols[r]) == row - r) {
valid = false;
break; // 发现冲突提前终止
}
}
if (!valid) continue;
cols[row] = col;
solveNQueens(n, row + 1, cols, results);
cols[row] = -1;
}
}
col - cols[r] == row - r
cols[r] - col == row - r
以组合总和问题为例(target=30,候选数组[2,3,5]):
方法 | 递归调用次数 | 执行时间(ms) |
---|---|---|
未剪枝版本 | 1892 | 4.7 |
剪枝版本 | 126 | 0.8 |
int alphaBeta(int depth, int alpha, int beta) {
if (depth == 0) return evaluate();
for (所有可能的走法) {
执行走法;
int score = -alphaBeta(depth-1, -beta, -alpha);
撤销走法;
if (score >= beta) return beta; // β剪枝
if (score > alpha) alpha = score;
}
return alpha;
}
// 示例:优先探索更有希望的路径
sort(branches.begin(), branches.end(),
[](const Node& a, const Node& b){
return a.heuristic > b.heuristic;
});
过度剪枝:
剪枝条件顺序错误:
// 正确顺序:先检查简单条件
if (simple_condition) continue;
if (complex_condition) continue;
状态恢复遗漏:
void backtrack() {
modify_state(); // 修改状态
backtrack();
restore_state(); // 必须恢复!
}
如果需要特定场景的剪枝实现(如数独求解、图着色问题),可以告诉我具体需求,我可以提供更针对性的代码实现和优化方案。