回溯
💡 核心思想
回溯法是一种系统地搜索所有解的方法:从一条路前进,遇到不满足条件就回退,尝试其他选择。它本质上是带剪枝的 DFS。经典应用:全排列、N 皇后、子集生成。
回溯的关键是"恢复现场"——你做了什么选择,回溯时就要撤销什么。比如在 N 皇后中,你放了一个皇后,标记了列和对角线,回溯时就要把这些标记清除。这是初学者最容易忽略的地方。
🎯 直觉理解
回溯就像走迷宫:你选择一条路,边走边做标记。走到死胡同,沿标记退回最近的岔路口,换条路走。关键是退回时要把标记擦掉,否则别的路也走不了了。
📝 算法流程
- 做出一个选择
- 递归搜索
- 撤销选择(恢复现场)
- 继续尝试下一个选择
$$\text{全排列数量:} n! \text{,回溯法的时间复杂度通常是指数级}$$
📊 复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间 | $O(n!)$ 或 $O(2^n)$(取决于问题) |
| 空间 | $O(n)$(递归深度) |
💻 参考实现(C++)
C++ (C++17)
#include <bits/stdc++.h>
using namespace std;
int n, col[15], ans = 0;
bool usedCol[30], usedDiag1[30], usedDiag2[30];
void dfs(int row) {
if (row == n) { ans++; return; }
for (int c = 0; c < n; c++) {
if (usedCol[c] || usedDiag1[row-c+n] || usedDiag2[row+c]) continue;
usedCol[c] = usedDiag1[row-c+n] = usedDiag2[row+c] = true;
dfs(row + 1);
usedCol[c] = usedDiag1[row-c+n] = usedDiag2[row+c] = false; // 恢复现场
}
}
int main() {
cin >> n;
dfs(0);
cout << ans << endl;
return 0;
}⚠️ 常见坑点
忘记恢复现场(撤销选择)
N皇后中对角线判断公式写错
没有剪枝导致 TLE
全局变量不清零导致多次调用出错
📚 相关题目
| 题目 | 来源 | 难度 | 备注 |
|---|---|---|---|
| P1219 八皇后 | 洛谷 | CSP-J | 回溯经典 |
| P1706 全排列问题 | 洛谷 | CSP-J | 回溯模板 |