#include <algorithm>
#include <cstdio>
#include <cstring>
bool flag[27];
int n;
int p[27];
int ans = 0;
void dfs(int k) {
if (k == n + 1) {
++ans;
return;
}
for (int i = 1; i <= n; ++i) {
if (flag[i]) continue;
if (k > 1 && i == p[k - 1] + 1) continue;
p[k] = i;
flag[i] = true;
dfs(k + 1);
flag[i] = false;
}
return;
}
int main() {
scanf("%d", &n);
dfs(1);
printf("%d\n", ans);
return 0;
}
16. (1 分) 当输入的 $n=3$ 的时候,程序输出的答案为 3。 17. 在 dfs 函数运行过程中,$k$ 的取值会满足 $1 \leq k \leq n+1$。 18. 删除第 19 行的 flag[i]=false;,对答案不会产生影响。
19. 当输入的 $n=4$ 的时候,程序输出的答案为( )。
20. 如果因为某些问题,导致程序运行第 25 行的 dfs 函数之前,数组 p 的初值并不全为 0,则对程序的影响是( )。
(本题为阅读程序 / 完善程序,完整题目与测试数据见平台)
这题核心是 DFS 回溯枚举排列,但加了一条‘禁止后一个数比前一个大 1'的限制。拿到题先想:n=3 时暴力列一下所有排列,划掉含'1,2'或'2,3'的,数剩下的个数;再想代码里哪几行在干这事,特别是 flag 的标记与恢复、p 数组的访问时机。
最容易把‘禁止连续递增对’理解成‘禁止相邻数字’或‘禁止递增’,导致把合法排列误删。其次是忽略回溯中 flag[i]=false 的作用,以为删了它只是少算几个,其实会导致整棵搜索树状态污染,合法解全漏。还有人在判断 p 数组初值影响时,只盯着 k=1 的情况,忘了 k>1 时 p[k-1] 早已在上一层被赋值,初值根本用不上。
算法 — DFS / 回溯法 / 排列生成
代码在枚举 1~n 的所有排列,但多了一个判断:if (k > 1 && i == p[k - 1] + 1) continue; 意思是:当前要填的位置 k,如果选的数字 i 恰好等于上一个位置 k-1 的数字加 1,就跳过。也就是说,排列里不能出现形如 (x, x+1) 的相邻对。