CSP-S 2025 提高级 第一轮(初赛) 阅读程序(1)
【题目】
cpp
01 #include <algorithm>
02 #include <cstdio>
03 #include <cstring>
04 bool flag[27];
05 int n;
06 int p[27];
07 int ans = 0;
08 void dfs(int k) {
09 if (k == n + 1){
10 ++ ans;
11 return;
12 }
13 for (int i = 1; i <= n; ++i) {
14 if (flag[i]) continue;
15 if (k > 1 && i == p[k - 1] + 1) continue;
16 p[k] = i;
17 flag[i] = true;
18 dfs(k + 1);
19 flag[i] = false;
20 }
21 return;
22 }
23 int main() {
24 scanf("%d", &n);
25 dfs(1);
26 printf("%d\n", ans);
27 return 0;
28 }
判断题
-
(1 分)当输入的 n=3n=3n=3 的时候,程序输出的答案为 333。
A. 正确
B. 错误
-
在 dfs 函数运行过程中,kkk 的取值会满足 1≤k≤n+11\le k\le n+11≤k≤n+1。
A. 正确
B. 错误
-
删除第 191919 行的 "flag[i]=false;",对答案不会产生影响。
A. 正确
B. 错误
单选题
-
当输入的 n=4n=4n=4 的时候,程序输出的答案为( )。
A. 11
B. 12
C. 24
D. 9
-
如果因为某些问题,导致程序运行到第 252525 行的 dfsdfsdfs 函数之前,数组 ppp 的初值并不全为 000,则对程序的影响是( )。
A. 输出的答案比原答案更小
B. 无法确定能输出的答案
C. 程序可能陷入死循环
D. 没有影响
-
假如删去第 141414 行的 "if(flag[i]) continue;",输入 333,得到的输出答案是( )。
A. 27
B. 3
C. 16
D. 12
【题目难度】:D
【题目考点】
1. 深搜回溯
【题目解析】
cpp
23 int main() {
24 scanf("%d", &n);
25 dfs(1);
26 printf("%d\n", ans);
27 return 0;
28 }
输入nnn,而后调用深搜函数,从111开始进行深搜,搜索结束后最后输出结果ansansans。
cpp
13 for (int i = 1; i <= n; ++i) {
14 if (flag[i]) continue;
15 if (k > 1 && i == p[k - 1] + 1) continue;
16 p[k] = i;
17 flag[i] = true;
18 dfs(k + 1);
19 flag[i] = false;
20 }
看dfs中的一般情况,iii从1循环到nnn,如果flag[i]为真,则continue,即直接开始下一次循环。先不看第15行,下面就是深搜求全排列的模板。当前在确定ppp数组中的第kkk个数,flag[i]表示数值iii是否已使用过。
- 如果数值iii已经使用过,则接下来不用iii,这是一个可行性剪枝。
- 否则就将
p[k]设为iii,而后将iii设为已使用过,即flag[i] = true,继续搜索确定ppp数组第k+1k+1k+1个数,递归调用dfs(k+1),调用结束后,将状态还原,再将iii设为未使用过,即flag[i] = false。
第15行也是一项剪枝,如果k>1k > 1k>1而且i == p[k-1]+1,就不将p[k]设为iii,限定k>1k>1k>1的原因是后面要取ppp数组下标k−1k-1k−1位置,如果k=1k=1k=1,那么k−1k-1k−1为0,p[0]不是已确定的ppp序列中的元素。当前要确定的是p[k],那么p[k-1]就是当前这一项的前一项。iii是p[k]待设定的值。那么这句话的意思是:如果当前这一项的值iii比前一项p[k-1]大1,那么就不将p[k]设为iii,也就是说在搜索全排列的过程中,排除了相邻两数后面的数比前面的数大1的情况。
cpp
09 if (k == n + 1){
10 ++ ans;
11 return;
12 }
递归出口,当kkk为n+1n+1n+1时,p[1]~p[k]都已经设好了值,存在一个满足条件的排列,ansansans计数增加。
该题求解的问题是:输入nnn,求1∼n1\sim n1∼n的排列中,不存在下一项比前一项大1的情况的排列的数量。
判断题
16. 当输入的 n=3n=3n=3 的时候,程序输出的答案为 333。
A. 正确
B. 错误
正确答案:A
按照字典序依次写出1∼31\sim 31∼3的全排列,排除其中存在下一项比前一项大1的排列:
1,2,31,2,31,2,3:不可行。
1,3,21,3,21,3,2:可行。
2,1,32,1,32,1,3:可行。
2,3,12,3,12,3,1:不可行。
3,1,23,1,23,1,2:不可行
3,2,13,2,13,2,1:可行共3个可行的排列,因此程序会输出3,本题叙述正确。
17. 在 dfs 函数运行过程中,kkk 的取值会满足 1≤k≤n+11\le k\le n+11≤k≤n+1。
A. 正确
B. 错误
正确答案:A
开始调用时传入实参1,形参kkk的值为1。kkk为1∼n1\sim n1∼n时都会进行递归调用
dfs(k+1),kkk为n+1n+1n+1时进入递归出口。因此kkk 的取值满足 1≤k≤n+11\le k\le n+11≤k≤n+1,本题叙述正确。
18. 删除第 191919 行的 "flag[i]=false;",对答案不会产生影响。
A. 正确
B. 错误
正确答案:B
如果删除
flag[i] = false,则深搜回溯缺少了回溯的过程,已经如果使用过数iii,在不使用iii时,不会将iii设为未使用过,下一次可以使用iii时就无法再使用iii。比如当输入nnn为3时,首先
dfs(1)中确定了p[1]=1,flag[1]=true。而后递归调用dfs(2),确定了p[2]=2, flag[2]=true,接着递归调用dfs(3),确定了p[3]=3, flag[3] = true,而后调用dfs(4),进入递归出口。回退到dfs(3),此时没有将flag[3]设为flase。再回退到dfs(2),这时iii变为3,判断flag[3]为真,直接进行下一次循环,无法将p[2]设为3。这样就无法找到可行的1,3,21,3,21,3,2这一排列,会导致计数减少,影响最值答案。因此本题叙述错误。
单选题
- 当输入的 n=4n=4n=4 的时候,程序输出的答案为( )。
A. 11
B. 12
C. 24
D. 9
正确答案:A
按照字典序依次查看1∼41\sim 41∼4的全排列,筛选出其中不存在下一项比前一项大1的排列。
可以先枚举前两个数的排列,再看后两个数可行的方案
第1、2个数 第3、4个数 1,2 不可行 1,3 4,2 1,4 3,2 2,1 4,3 2,3 不可行 2,4 1,3或3,1 3,1 4,2 3,2 1,4或4,1 3,4 不可行 4,1 3,2 4,2 1,3 4,3 2,1 共11种可行的排列,本题选A。
- 如果因为某些问题,导致程序运行到第 252525 行的 dfsdfsdfs 函数之前,数组 ppp 的初值并不全为 000,则对程序的影响是( )。
A. 输出的答案比原答案更小
B. 无法确定能输出的答案
C. 程序可能陷入死循环
D. 没有影响
正确答案:D
每次确定第kkk位置的值,都会使用当前确定的值iii覆盖
p[k]的值。在确定p[k]时,可能用到的p[k-1]位置的值是这一次dfs中先前确定的值,与ppp数组的初值无关,因此ppp的初值是否为000并不影响程序,选D。
- 假如删去第 141414 行的 "if(flag[i]) continue;",输入 333,得到的输出答案是( )。
A. 27
B. 3
C. 16
D. 12
正确答案:C
如果删去
if(flag[i]) continue;,那么即便数值iii在先前已经使用过,还可以再次使用。ppp数组中可以同时存在相同的数值,但还是要满足不存在下一项比前一项大1的情况。按字典序枚举所有由1∼31\sim 31∼3生成的可能存在重复元素的序列,排除存在下一项比前一项大1的序列,得到的可行的排列有:
1,1,11,1,11,1,1
1,1,31,1,31,1,3
1,3,11,3,11,3,1
1,3,21,3,21,3,2
1,3,31,3,31,3,3
2,1,12,1,12,1,1
2,1,32,1,32,1,3
2,2,12,2,12,2,1
2,2,22,2,22,2,2
3,1,13,1,13,1,1
3,1,33,1,33,1,3
3,2,13,2,13,2,1
3,2,23,2,23,2,2
3,3,13,3,13,3,1
3,3,23,3,23,3,2
3,3,33,3,33,3,3共16个可行的排列,选C。