🧭 阶段1:递归与基本DFS(深度优先搜索)
🎯 一、训练营目标
| 目标序号 |
目标内容 |
| 1 |
理解递归函数的定义、结构与执行机制(栈式调用过程) |
| 2 |
掌握DFS(深度优先搜索)的实现方式,能够应用于图、网格搜索 |
| 3 |
掌握递归中的状态传递与返回路径,能解决多路径枚举类问题 |
| 4 |
学会设计递归出口与剪枝优化,避免爆栈和超时问题 |
| 5 |
熟悉基础递归/DFS题型在CCC J4/J5中出现的形式与变种 |
🧩 二、题型分类及典型代表
| 类别 |
题型描述 |
代表题目 |
涉及考点 |
| 数组/网格DFS |
在二维网格中找路径/统计路径数量 |
✅ CCC 2014 J4 - Party Invitation Grid |
递归 + 网格移动 + 边界处理 |
| 树状递归结构 |
多分支递归结构体/枚举 |
✅ 洛谷 P1217 [排列] ABC 字符串变换 |
多分支递归、字符处理 |
| 子集/组合枚举 |
枚举所有路径、排列、组合 |
✅ 洛谷 P1036 [选数] |
回溯、组合选择、剪枝 |
| 模拟DFS过程 |
根据规则模拟走法,判断是否能走通 |
✅ 洛谷 P1596 [海底探险] |
DFS过程可视化、剪枝 |
📘 三、训练营详细内容
✅ 1. 递归基础语法
- 函数调用自身(直接或间接)
- 栈帧调用与返回值处理
- 基础递归模型(如阶乘、斐波那契)
int factorial(int n) {
if (n == 1) return 1; // 递归出口
return n * factorial(n - 1); // 递归式
}
✅ 2. DFS结构与状态空间表示
- 利用函数递归完成状态空间遍历
- 用数组/矩阵表示搜索状态
- 每次搜索尝试一个方向(图/网格)
void dfs(int x, int y) {
if (x == endX && y == endY) {
count++;
return;
}
for (int d = 0; d < 4; ++d) {
int nx = x + dx[d], ny = y + dy[d];
if (合法(nx, ny)) {
vis[nx][ny] = true;
dfs(nx, ny);
vis[nx][ny] = false; // 回溯
}
}
}
✅ 3. 剪枝与边界控制
- 越界判断
- 已访问判断(visited数组)
- 不满足条件提前返回(剪枝)
✅ 4. 状态参数设计
- 参数携带路径长度、值和等信息
- 利用回溯撤回状态(如:组合选择)
✅ 5. 栈深分析与爆栈预防
- 常规递归支持深度约 10^4 级
- 若搜索状态数过大,建议使用非递归写法或限制搜索范围
🧪 四、典型训练题目推荐(附平台)
| 题目名称 |
类型 |
平台 |
说明 |
| CCC 2014 J4 - Party Invitation Grid |
网格DFS |
CEMC |
从(0,0)走到(m,n),路径计数 |
| 洛谷 P1036 - 选数 |
组合递归 |
洛谷 |
选K个数使其和为质数 |
| 洛谷 P1596 - 海底探险 |
模拟DFS |
洛谷 |
DFS走迷宫+剪枝 |
| 洛谷 P1217 - ABC字符串排列 |
枚举DFS |
洛谷 |
多分支字符递归 |
| 洛谷 P1460 - 旅行家的预算 |
回溯路径枚举 |
洛谷 |
多路径决策 |
⏱️ 五、训练安排建议
| 任务编号 |
内容安排 |
建议目标 |
| T1 |
✅ 学习递归结构与基本递归函数实现 ✅ 实现斐波那契数列、阶乘、幂运算等 |
理解递归机制,掌握调用栈工作原理 |
| T2 |
✅ 学习DFS搜索模板 ✅ 尝试实现简单图/网格走法 |
掌握网格DFS模板,能进行路径枚举与计数 |
| T3 |
✅ 加入visited数组与边界控制 ✅ 学习回溯与剪枝 |
学会写出合法搜索,掌握搜索空间控制与回退机制 |
| T4 |
✅ 综合练习题目 ✅ 实战演练典型CCC真题 |
初步形成完整DFS解题能力,掌握路径搜索与优化技巧 |
🧠 六、总结归纳
| 知识点 |
说明 |
常见错误 |
| 递归出口 |
避免无限递归,保证存在停止条件 |
少写return或写错出口条件 |
| DFS方向枚举 |
通常上下左右四个方向 |
方向数组写错,漏判断边界 |
| visited数组 |
标记已走过路径,避免重复访问 |
忘记撤销,导致错误剪枝 |
| 剪枝优化 |
用于提升效率,过滤非法状态 |
剪枝条件过严漏解 |
| 回溯处理 |
恢复现场状态,确保其他分支正常进行 |
忘记回溯导致错误路径传播 |
🎁 附:通用DFS模板(C++)
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};
int n, m;
bool vis[105][105];
char grid[105][105];
void dfs(int x, int y) {
vis[x][y] = true;
for (int d = 0; d < 4; d++) {
int nx = x + dx[d];
int ny = y + dy[d];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && !vis[nx][ny] && grid[nx][ny] != '#') {
dfs(nx, ny);
}
}
}