试卷解析 · 理解每一步
2022 洛谷 SCP-J 第一轮模拟题(入门级 C++)
CSPJ-2022-R1-Mock-Luogu
试卷解析总览,可直接查看每题答案与解析。
第 1 题(单选题,2 分)
( )。
正确答案C
解析详情
,而 ,故选 C。
第 2 题(单选题,2 分)
若逻辑变量 A、C 为真,B、D 为假,以下逻辑表达式的值为假的是( )。
正确答案D
解析详情
代入 ,D 式含有合取项 ,整体必为假;其余三式均为真。
第 3 题(单选题,2 分)
小恺编写如下朴素递归函数计算斐波那契数列第 项对 10000 的余数。在 128 MB、1 秒限制下运行 `f(12345)`,最可能首先发生什么问题?
int f(int x) {
if (x <= 2) return 1;
int ans = f(x - 1) + f(x - 2);
ans %= 10000;
return ans;
}正确答案A
解析详情
朴素斐波那契递归会重复计算,调用次数按指数级增长;`f(12345)` 远超 1 秒,通常先超时。
第 4 题(单选题,2 分)
表达式 `a+b*(c-d)/e-f` 的后缀表达式为( )。
正确答案B
解析详情
先将 `(c-d)` 写为 `cd-`,再依次完成乘、除、加、减,得到 `abcd-*e/+f-`。
第 5 题(单选题,2 分)
一段时长 4 分钟、每秒 10 帧、每帧 像素、32 位真彩色且画面未压缩、没有音频的视频,约需多少存储空间?
正确答案A
解析详情
字节约为 21.1 GiB,因此选 21 GiB。
第 6 题(单选题,2 分)
下图二叉树的后序遍历是( )。

正确答案C
解析详情
后序顺序是左子树、右子树、根:B 子树为 `DFEB`,C 子树为 `C`,最后访问 A,得到 `DFEBCA`。
第 7 题(单选题,2 分)
五个本质不同的点在没有重边或自环时,能组成多少个不同的无向图?
正确答案B
解析详情
5 个标号点共有 inom{5}{2}=10 条可能的无向边,每条边独立选择有或无,共 个图。
第 8 题(单选题,2 分)
元素 `a,b,c,d,e,f` 依次入栈,下列不合法的出栈序列为( )。
正确答案D
解析详情
D 中弹出 `e,d` 后,栈内从顶到底为 `c,b,a`;不可能越过 `c` 先弹出 `b`,故不合法。
第 9 题(单选题,2 分)
同时扔出 3 枚完全相同的六面骰子,将点数排序后有多少种不同结果?
正确答案B
解析详情
排序后的三个点数等价于从 1~6 中可重复选 3 个,方案数为 inom{6+3-1}{3}=56。
第 10 题(单选题,2 分)
从磁盘文件输入很大的二维数组时,按行读与按列读相比,在输入效率上( )。
正确答案D
解析详情
访问效率取决于数组采用行优先还是列优先存储;连续访问内存通常能更好利用缓存和磁盘缓冲。
第 11 题(单选题,2 分)
不考虑稳定性,下列排序方法中平均时间复杂度最大的是( )。
正确答案A
解析详情
插入排序平均时间复杂度为 ;归并和快速排序平均为 ,希尔排序通常也优于直接插入排序。
第 12 题(单选题,2 分)
将 `12,23,-1,19,117,-103,79,602` 按从大到小排列,每次可交换任意两个元素,最少需交换几次?
正确答案B
解析详情
把当前位置到目标降序位置的映射分解成环,8 个元素形成 3 个环,最少交换数为 。
第 13 题(单选题,2 分)
3 名男生和 3 名女生围成一圈,同性不相邻;旋转可重合视为同一种方案,共有多少种方案?
正确答案C
解析详情
先固定 3 名男生的圆排列,有 种;3 名女生放入三个间隙有 种,共 12 种。
第 14 题(单选题,2 分)
以下关于 C++ 字符串的说法,错误的是( )。
正确答案D
解析详情
`strlen` 接收 C 风格字符指针,不能直接传入 `std::string`;应使用 `s.size()` 或 `s.length()`。
第 15 题(单选题,2 分)
中国计算机协会成立于( )年。
正确答案B
解析详情
按原卷答案,中国计算机协会成立年份为 1962 年,因此选择 B。
二、阅读程序(1)
1 | #include <iostream>
2 | using namespace std;
3 | const int MAXN = 1000050;
4 | int n, a[MAXN], a1[MAXN], b[MAXN], lim;
5 | void solve1() {
6 | for (int i = 1; i <= n; i++)
7 | b[a[i]]++; //①
8 | for (int i = 1; i <= lim; i++) {
9 | if (b[i]) //②
10 | cout << i << " ";
11 | }
12 | cout << endl;
13 | }
14 | void solve2() {
15 | int cnt = 0, flag;
16 | for (int i = 1; i <= n; i++) {
17 | flag = false;
18 | for (int j = 1; j <= n - 1; j++) {
19 | if (a[j] > a[j + 1]) {
20 | swap(a[j], a[j + 1]);
21 | cnt++;
22 | flag = true;
23 | }
24 | }
25 | //if (flag==false)
26 | // break;
27 | }
28 | for (int i = 1; i <= n; i++)
29 | cout << a[i] << " ";
30 | cout << endl;
31 | }
32 | int main() {
33 | cin >> n;
34 | for (int i = 1; i <= n; i++) {
35 | cin >> a[i];
36 | a1[i] = a[i]; //③
37 | lim = max(a[i], lim); //④
38 | }
39 | solve1();
40 | for (int i = 1; i <= n; i++)
41 | a[i] = a1[i];
42 | solve2();
43 | return 0;
44 | }已知 。完成下列题目。
第 16 题(判断题,2 分)
`solve2` 实现了选择排序。( )
正确答案错误
解析详情
`solve2` 反复比较并交换相邻逆序元素,实现的是冒泡排序,不是选择排序。
第 17 题(判断题,2 分)
`solve1` 的时间复杂度为 ,其中 是 的最大值。( )
正确答案正确
解析详情
统计 个元素需 ,再扫描到最大值 需 ,总复杂度为 。
第 18 题(判断题,2 分)
输入 `7 2 3 5 7 1 4 6` 时,`solve2` 中的 `cnt` 最终为 9。( )
正确答案错误
解析详情
冒泡排序的交换次数等于逆序对数;序列 `2,3,5,7,1,4,6` 共有 7 个逆序对,不是 9。
第 19 题(判断题,2 分)
移除 `solve2` 中注释行的双斜杠,不会影响输出结果。( )
正确答案正确
解析详情
取消注释后,某一轮没有交换时提前退出;此时数组已经有序,所以最终输出不变。
第 20 题(单选题,3 分)
哪组 的数据会使 `solve1` 与 `solve2` 输出不同?
正确答案C
解析详情
C 中存在重复值,`solve1` 的 `if (b[i])` 每个不同值只输出一次,而 `solve2` 会保留并输出所有重复元素。
第 21 题(单选题,3 分)
若要使 `solve1` 和 `solve2` 输出相同,应修改哪一处?
正确答案B
解析详情
应修改 ②,使每个值按 `b[i]` 的次数重复输出,例如用循环消耗计数;这样才能与冒泡排序结果一致。
二、阅读程序(2)
1 | #include <cstdio>
2 | #include <cstring>
3 |
4 | const int maxn = 1003;
5 |
6 | int type, n, m;
7 | char s[maxn], t[maxn];
8 |
9 | int main() {
10 | scanf("%d %s %s", &type, t, s);
11 | n = strlen(s); m = strlen(t);
12 | if (type < 2) {
13 | for (int i = 0; i < m; ++i) s[i] = t[i];
14 | } else if (type == 2) {
15 | strcpy(s, t);
16 | // 提示:如果此时调用 printf("%s\n", s),则本次输出结果为整个 t 字符串和换行,没有其他字符。
17 | } else {
18 | for (int i = 0; i < m; i += 4) {
19 | unsigned int code = 0, pos = i;
20 | for (int j = 1; pos < i+4; j*=100, ++pos) {
21 | if (pos == m) break;
22 | code += t[pos] * j;
23 | }
24 | pos = i;
25 | while (code != 0) {
26 | s[pos++] = code % 100;
27 | code /= 100;
28 | }
29 | }
30 | }
31 | for (int i = 0; i < n; ++i) printf("%c", s[i]);
32 | printf("\n");
33 | }输入保证 `t` 不长于 `s`,两字符串仅含大小写字母且非空,`type=1,2,3`。
第 22 题(判断题,2 分)
将程序中所有 `<` 改为 `!=`,对所有合法输入的输出不变。( )
正确答案错误
解析详情
把所有 `<` 改为 `!=` 后,`type < 2` 变成 `type != 2`,会把 `type=3` 错误地送入第一分支,输出会改变。
第 23 题(判断题,2 分)
输入 `1 xyz abcd` 时输出 `xyzd`。( )
正确答案正确
解析详情
`type=1` 时把 `t` 的三个字符 `xyz` 写到 `s` 前三位,原来的第四个字符 `d` 保留,因此输出 `xyzd`。
第 24 题(判断题,2 分)
输入 `1 xyz abcd` 与 `2 xyz abcd` 的输出相同。( )
正确答案错误
解析详情
`type=2` 的 `strcpy` 会在 `xyz` 后写入 NUL 结束符,第 4 次 `%c` 输出空字符;这与 `type=1` 保留 `d` 的 `xyzd` 不同。
第 25 题(判断题,2 分)
将第 25~28 行 `while` 替换为判断条件和循环体不变的 `do-while`,输出一定不变。( )
正确答案正确
解析详情
每个分组由非空字母组成,构造出的 `code` 一定非零,因此原 `while` 至少执行一次,改为 `do-while` 不改变结果。
第 26 题(单选题,2 分)
若第 13 行改为 `for (int i=0; i<strlen(t); ++i) s[i]=t[i];` 且 `type=1`,用 分别表示 `s,t` 长度,时间复杂度为( )。
正确答案B
解析详情
循环条件每次调用 `strlen(t)`,单次扫描为 ,共执行 次;加上最终输出 个字符,总计 。
第 27 题(单选题,3 分)
分别输入哪组选项中的两组数据,输出不同?
正确答案C
解析详情
小写 `d,e` 的 ASCII 值不小于 100,`type=3` 的百进制拆分会改变它们;`type=1` 直接复制,所以 C 的两次输出不同。
二、阅读程序(3)
1 | #include <iostream>
2 | using namespace std;
3 |
4 | const int INF = 1000000000;
5 | #define Front 0
6 | #define Back 1
7 | #define Left 2
8 | #define Right 3
9 | #define Up 4
10 | #define Down 5
11 | int w[6], a[1003][1003];
12 | const int way1[] = {Up, Right, Down, Left};
13 | const int way2[] = {Up, Front, Down, Back};
14 | const int way3[] = {Left, Front, Right, Back};
15 | int get_max(int &a, int b) {
16 | return a = max(a, b);
17 | }
18 | int right_rotate(int &u) {
19 | for (int i = 0; i < 4; ++ i)
20 | if (u == way1[i])
21 | return u = way1[(i + 1) % 4];
22 | return u;
23 | }
24 | int front_rotate(int &u) {
25 | for (int i = 0; i < 4; ++ i)
26 | if (u == way2[i])
27 | return u = way2[(i + 1) % 4];
28 | return u;
29 | }
30 | const int anchorX = Up;
31 | const int anchorY = Front;
32 | const int anchorZ = Right;
33 | int find_down(int u, int v) {
34 | if (u == Down || u == Up) return anchorX ^ (u == Up);
35 | if (v == Down || v == Up) return anchorY ^ (v == Up);
36 | for (int i = 0; i < 4; ++ i)
37 | if (u == way3[i])
38 | return anchorZ ^ (v == way3[(i + 1) % 4]);
39 | return -1;
40 | }
41 | int n, m, dp[1003][1003][6][6];
42 | int main() {
43 | cin >> n >> m;
44 | for (int i = 0; i < n; ++ i)
45 | for (int j = 0; j < m; ++ j)
46 | cin >> a[i][j];
47 | for (int i = 0; i < 6; ++ i)
48 | cin >> w[i];
49 | for (int i = 0; i < n; ++ i)
50 | for (int j = 0; j < m; ++ j)
51 | for (int a = 0; a < 6; ++ a)
52 | for (int b = 0; b < 6; ++ b)
53 | dp[i][j][a][b] = -INF;
54 | dp[0][0][anchorX][anchorY] = a[0][0] * w[Down];
55 | for (int i = 0; i < n; ++ i)
56 | for (int j = 0; j < m; ++ j)
57 | for (int p = 0; p < 6; ++ p)
58 | for (int q = 0; q < 6; ++ q)
59 | if (dp[i][j][p][q] != -INF) {
60 | int x = dp[i][j][p][q];
61 | int u1 = p, v1 = q;
62 | right_rotate(u1);
63 | right_rotate(v1);
64 | get_max(dp[i][j + 1][u1][v1],
65 | x + w[find_down(u1, v1)] * a[i][j + 1]);
66 | int u2 = p, v2 = q;
67 | front_rotate(u2);
68 | front_rotate(v2);
69 | get_max(dp[i + 1][j][u2][v2],
70 | x + w[find_down(u2, v2)] * a[i + 1][j]);
71 | }
72 | int ans = -INF;
73 | for (int p = 0; p < 6; ++ p)
74 | for (int q = 0; q < 6; ++ q)
75 | ans = max(ans, dp[n - 1][m - 1][p][q]);
76 | printf("%d\n", ans);
77 | return 0;
78 | }输入数据的绝对值均不超过 。
第 28 题(判断题,2 分)
存在合法输入,使某次 `find_down` 返回 -1。( )
正确答案错误
解析详情
程序只沿相邻格滚动立方体,`u,v` 始终表示两个互相垂直的面;第三个朝向可唯一确定,所以 `find_down` 不会走到 -1。
第 29 题(判断题,2 分)
程序时间复杂度为 。( )
正确答案错误
解析详情
网格有 个位置,每格枚举固定的 个朝向并做常数次转移,因此时间复杂度为 。
第 30 题(判断题,2 分)
对任意 ,先 `front_rotate` 再 `right_rotate` 与相反顺序的最终值相同。( )
正确答案错误
解析详情
两种旋转绕不同轴进行,一般不满足交换律;选择某些侧面即可得到不同的最终朝向,因此断言错误。
第 31 题(单选题,3 分)
将 `anchorX,anchorY,anchorZ` 依次更换为哪项时,对全部合法数据输出不变?
正确答案D
解析详情
D 的 `Down, Right, Front` 仍给出了与原锚点一致的一组合法互相垂直朝向,只是换用了等价编码,因此所有底面权值转移不变。
第 32 题(单选题,2 分)
下列输入的输出为( )。
5 5
2 8 15 1 10
5 19 19 3 5
6 6 2 8 2
12 16 3 8 17
12 5 3 14 13
1 1 1 1 1 1正确答案B
解析详情
按向右和向下滚动后的底面权值更新 36 种朝向状态,到达右下角的最大值为 97,故选 B。
第 33 题(单选题,2 分)
下列输入的输出为( )。
2 5
2 8 15 3 10
5 19 19 3 5
1 2 3 4 5 6正确答案A
解析详情
对 网格逐列进行向右滚动转移,结合权值 `1 2 3 4 5 6`,终点最大加权和为 194。
三、完善程序(1)支付问题
有 种各一张、面值为 的纸币,求能支付多少种非零金额。,面值总和不超过 5000。
#include <iostream>
using namespace std;
const int MAXN = 210, MAXM = 5010;
int n, m, f[MAXM], a[MAXN];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) { cin >> a[i]; ①; }
②;
for (int i = 1; i <= n; i++)
③
f[j] = ④;
int ans = 0;
for (int i = 1; i <= m; i++) if (⑤) ans++;
cout << ans;
return 0;
}第 34 题(单选题,3 分)
① 处应填( )。
n += a[i]m += a[i]n = a[i]m = a[i]正确答案B
解析详情
`m` 用来记录所有纸币面值之和,后续枚举金额范围为 1~`m`,所以应执行 `m += a[i]`。
第 35 题(单选题,3 分)
② 处应填( )。
f[0] = 1f[1] = 1a[0] = 1a[1] = 1正确答案A
解析详情
金额 0 在不选择任何纸币时可达,是 0/1 背包的初始状态,因此设 `f[0]=1`。
第 36 题(单选题,3 分)
③ 处应填( )。
for (int j = a[i]; j <= n; j++)for (int j = n; j >= a[i]; j--)for (int j = a[i]; j <= m; j++)for (int j = m; j >= a[i]; j--)正确答案D
解析详情
每张纸币只能使用一次,金额必须从总和 `m` 递减到 `a[i]`,即选 D。
第 37 题(单选题,3 分)
④ 处应填( )。
f[j - 1] + 1f[j - a[i]] + 1f[j] || f[j - a[i]]f[j] && f[j - a[i]]正确答案C
解析详情
金额 `j` 原本可达,或金额 `j-a[i]` 可达并加入当前纸币,都能使 `j` 可达,因此为 `f[j] || f[j-a[i]]`。
第 38 题(单选题,3 分)
⑤ 处应填( )。
f[i]f[i - 1]f[i] == f[i + 1]f[i] == f[i - 1]正确答案A
解析详情
处理完全部纸币后,`f[i]` 为真表示金额 `i` 可支付,遍历 1~`m` 直接统计 `f[i]`。
三、完善程序(2)凑出 17
给 个互不相同的正整数 ,在每个数前放 `+` 或 `-`,判断是否能使算式值为 17。
#include <cstdio>
using namespace std;
const int maxn = 25, aim = 17;
int n, a[maxn];
bool ans;
int getBit(const int s, int p) { return ①; }
int main() {
scanf("%d", &n);
for (②) scanf("%d", a + i);
for (int s=0, upperBound=③; s<=upperBound; ++s) {
④;
for (int j=0; j<n; ++j) if (getBit(s,j)==1) sum += a[j];
else { ⑤; }
if (int(sum)==aim) { ans=true; break; }
}
printf("%s\n", ans ? "Yes" : "No");
}第 39 题(单选题,3 分)
① 处应填( )。
(s >> p) & 1(s << p) & 1s & (1 << p) & 1s & (1 >> p) & 1正确答案A
解析详情
右移 `p` 位后与 1,恰好取得整数 `s` 的第 `p` 位,结果为 0 或 1。
第 40 题(单选题,3 分)
② 处应填( )。
int i = 0; i <= n; ++iint i = 1; i <= n; ++iint i = 0; i < n; ++iint i = 1; i < n; ++i正确答案C
解析详情
数组按 0~`n-1` 存放 个输入,应使用 `int i=0; i<n; ++i`。
第 41 题(单选题,3 分)
③ 处应填( )。
1 << n(1 << n) | 1(1 << n) + 1(1 << n) - 1正确答案D
解析详情
个数的正负号共有 种组合,状态编号为 0~,上界应为 `(1<<n)-1`。
第 42 题(单选题,3 分)
④ 处应填( )。
int sum = 0unsigned long long sum = 0unsigned short sum = 0unsigned int sum = 0正确答案B
解析详情
最多 20 个不超过 的数求和,绝对值可能达到 ;给出的类型中只有 `unsigned long long` 能覆盖所需范围。
第 43 题(单选题,3 分)
⑤ 处应填( )。
sum = a[j] + sumsum = a[j] - sumsum = -a[j] + sumsum = -a[j] - sum正确答案C
解析详情
当前位为 0 时应给 `a[j]` 加负号,即执行 `sum=-a[j]+sum`,与位为 1 时的加法对应。