C++ 竞赛入门

前进与回溯

前进与回溯

本节学会什么

  • 区分递归调用前后的两段代码
  • 理解“前进时做选择,返回时撤销选择”
  • 用输出顺序观察回溯

为什么需要它

递归不仅能计算一个值,也能按路径探索选择。调用下一层前保存当前选择,下一层返回后恢复现场,这个过程称为回溯,是之后搜索和枚举的核心。

最小可运行示例

#include <iostream>

void walk(int depth, int n) {
    if (depth > n) {
        return;
    }
    std::cout << "enter " << depth << '\n';
    walk(depth + 1, n);
    std::cout << "leave " << depth << '\n';
}

int main() {
    walk(1, 3);
    return 0;
}

输出先是 enter 1、enter 2、enter 3,然后才是 leave 3、leave 2、leave 1。递归调用前是前进,调用返回后是回溯;后面的语句不是没有执行,而是等更深层完成后再执行。

递归过程图:walk 从第一层进入到边界,再依次返回第三层、第二层和第一层

关键过程与易错点

  • 在递归调用前修改状态,必须在调用后恢复状态,否则下一个分支会带着上一个分支的数据。
  • 输出或记录答案常在边界条件处完成,因为那时一个完整选择已经形成。
  • 不要把“回溯”理解为自动撤销所有变量。只有你明确写出的恢复操作才会发生。
  • 调试时给 depth 加缩进输出,比盯着很多次函数调用更直观。

动手检查

将示例改成先输出 depth、递归、再输出 depth,用空格分隔。对 n = 3 写出预期序列后再运行。

定义全局整数 chosen,递归前加一、递归后减一,在每层输出它,确认返回时恢复到了进入该层前的值。

立即练习

难度 题目 题解 训练目标
基础 洛谷 P2386:放苹果 - 先理解“当前放多少个苹果”,再递归处理剩下的苹果和盘子。
基础 洛谷 P1036:选数 题解 观察“选择下一个数”和“撤销选择”的递归框架。
巩固 洛谷 P1706:全排列问题 题解 used 数组记录选择,并在返回时恢复状态。