C++ 竞赛入门
递归的应用:枚举
递归枚举
本节学会什么
- 用递归枚举每个位置的选择
- 在边界处输出一组完整方案
- 用数组保存当前方案并正确回溯
为什么需要它
“每个数选或不选”“从若干数中选出若干个”“排列所有数字”都有许多方案。递归可以把第 pos 步要做的选择写清楚,再让下一层处理剩余位置。
最小可运行示例
输出 1 到 n 的所有选取方案:
#include <iostream>
int chosen[25];
void enumerate(int pos, int n) {
if (pos > n) {
for (int i = 1; i <= n; i += 1) {
if (chosen[i]) {
std::cout << i << ' ';
}
}
std::cout << '\n';
return;
}
chosen[pos] = 0;
enumerate(pos + 1, n);
chosen[pos] = 1;
enumerate(pos + 1, n);
}
int main() {
int n;
std::cin >> n;
enumerate(1, n);
return 0;
}
每个位置有“不选”和“选”两个分支,所以总方案数是 2^n。当 pos > n 时,所有位置都已经决定,才能输出一组完整方案。
关键过程与易错点
- 先定义状态含义:这里
chosen[i]为1表示选第i个数。 - 一个递归层要覆盖所有可能选择;漏一个分支就会漏方案。
- 每次递归都让
pos加一,边界是pos > n。 - 输出空集时会得到空行,这是“一个元素也不选”的正常方案;题目是否需要它由题意决定。
n增加一点,方案数会成倍增长。先确认数据范围,不能把n = 30当作小题。
动手检查
输入 n = 2,手写四种选法,再比对输出是否各出现一次。将输出改成 0/1 序列,检查每一位的选择是否正确。
把“选或不选”改为“当前位置填 0 或 1”,输出长度为 n 的所有二进制串。
立即练习
| 难度 | 题目 | 题解 | 训练目标 |
|---|---|---|---|
| 基础 | 洛谷 P1157:组合的输出 | 题解 | 枚举固定长度的组合。 |
| 巩固 | 洛谷 P1706:全排列问题 | 题解 | 用 used 数组保证每个数只选一次。 |
| 巩固 | 洛谷 P1605:迷宫 | 题解 | 网格 DFS、访问标记与回溯恢复,不需要冲突剪枝。 |
| 挑战 | 洛谷 P1219:八皇后 | 题解 | 在枚举中加入合法性判断与剪枝。 |