GESP 客观题评测系统

试卷解析 · 理解每一步

2024 CCF CSP-J 第一轮(入门级 C++)

CSPJ-2024-R1

试卷解析总览,可直接查看每题答案与解析。

一、单项选择题

(共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项)

第 1 题(单选题,2 分)

32 位 int 类型的存储范围是?( )

A.
-2147483647 ~ +2147483647
B.
-2147483647 ~ +2147483648
C.
-2147483648 ~ +2147483647
D.
-2147483648 ~ +2147483648

正确答案C

解析详情
答案 C有符号整数的二进制表示
思路与推导
  1. 32 位有符号 int 按补码表示时,最高位的权值为 −231-2^{31},其余 31 位最大和为 231−12^{31}-1,所以范围是 -2147483648 到 2147483647。

第 2 题(单选题,2 分)

计算 (148−10102)∗D16−11012(14_8 - 1010_2) * D_{16} - 1101_2 的结果,并选择答案的十进制值:()

A.
13
B.
14
C.
15
D.
16

正确答案A

解析详情
答案 A不同进制数的转换与运算
思路与推导
  1. 148=1214_8=12,10102=101010_2=10,D16=13D_{16}=13,11012=131101_2=13。

  2. 因此 (12−10)×13−13=13(12-10)\times13-13=13。

第 3 题(单选题,2 分)

某公司有 10 名员工,分为 3 个部门:A 部门有 4 名员工、B 部门有 3 名员工、C 部门有 3 名员工。现需要从这 10 名员工中选出 4 名组成一个工作小组,且每个部门至少要有 1 人。问有多少种选择方式?()

A.
120
B.
126
C.
132
D.
238

正确答案B

解析详情
答案 B分类计数与组合数
思路与推导
  1. 4 人覆盖 3 个部门,人数分配只能是 (2,1,1)(2,1,1)。

  2. 多出的 1 人来自 A、B、C 时,方案数分别为 (42)(31)(31)=54\binom{4}{2}\binom{3}{1}\binom{3}{1}=54、(41)(32)(31)=36\binom{4}{1}\binom{3}{2}\binom{3}{1}=36、(41)(31)(32)=36\binom{4}{1}\binom{3}{1}\binom{3}{2}=36,合计 126。

第 4 题(单选题,2 分)

以下哪个序列对应数字 0 至 8 的 4 位二进制格雷码(Gray code)?()

A.
0000, 0001, 0011, 0010, 0110, 0111, 0101, 1000
B.
0000, 0001, 0011, 0010, 0110, 0111, 0100, 0101
C.
0000, 0001, 0011, 0010, 0100, 0101, 0111, 0110
D.
0000, 0001, 0011, 0010, 0110, 0111, 0101, 0100

正确答案D

解析详情
答案 D格雷码
思路与推导
  1. 格雷码要求相邻两个码字恰好只有 1 位不同。D 中从 0000 到 0100 的每次相邻变化都只翻转 1 位;题干列出的 8 个码字实际对应 0 至 7,按所给序列应选 D。

第 5 题(单选题,2 分)

记 1KB 为 1024 字节(byte)、1MB 为 1024KB,那么 1MB 是多少二进制位(bit)?()

A.
1000000
B.
1048576
C.
8000000
D.
8388608

正确答案D

解析详情
答案 D存储容量单位换算
思路与推导
  1. 1MB=1024×1024=10485761\text{MB}=1024\times1024=1048576 字节,每字节 8 位,所以共有 1048576×8=83886081048576\times8=8388608 位。

第 6 题(单选题,2 分)

以下哪个不是 C++ 中的基本数据类型?( )

A.
int
B.
float
C.
struct
D.
char

正确答案C

解析详情
答案 CC++ 基本数据类型
思路与推导
  1. int、float 和 char 都是 C++ 的基本数据类型;struct 是定义结构体这种复合数据类型的关键字,本身不是基本数据类型。

第 7 题(单选题,2 分)

以下哪个不是 C++ 中的循环语句?( )

A.
for
B.
while
C.
do-while
D.
repeat-until

正确答案D

解析详情
答案 DC++ 循环语句
思路与推导
  1. C++ 提供 for、while 和 do-while 三种循环结构,没有 repeat-until 语句。

第 8 题(单选题,2 分)

在 C/C++ 中,(char)('a'+13)与下面的哪一个值相等?()

A.
'm'
B.
'n'
C.
'z'
D.
'3'

正确答案B

解析详情
答案 B字符编码与算术运算
思路与推导
  1. 按题目采用的常见 ASCII 编码,小写英文字母连续,'a' 加 13 就是向后移动 13 个字符位置,得到 'n';再转换为 char 后值不变。

第 9 题(单选题,2 分)

假设有序表中有 1000 个元素,则用二分法查找元素 X 最多需要比较( )次。

A.
25
B.
10
C.
7
D.
1

正确答案B

解析详情
答案 B二分查找的比较次数
思路与推导
  1. 二分查找每比较一次将范围缩小约一半。因为 29=512<1000≤210=10242^9=512<1000\le 2^{10}=1024,最坏情况下最多比较 10 次。

第 10 题(单选题,2 分)

下面的哪一个不是操作系统名字?()

A.
Notepad
B.
Linux
C.
Windows
D.
macOS

正确答案A

解析详情
答案 A操作系统与应用软件
思路与推导
  1. Linux、Windows 和 macOS 都是操作系统;Notepad 是运行在操作系统之上的文本编辑应用程序。

第 11 题(单选题,2 分)

在无向图中,所有顶点的度数之和等于()。

A.
图的边数
B.
图的边数的两倍
C.
图的顶点数
D.
图的顶点数的两倍

正确答案B

解析详情
答案 B无向图的握手定理
思路与推导
  1. 无向图的每条边分别给两个端点贡献 1 度,因此所有顶点度数之和为边数的两倍,即 ∑vdeg⁡(v)=2∣E∣\sum_v\deg(v)=2|E|。

第 12 题(单选题,2 分)

已知二叉树的前序遍历为 [A,B,D,E,C,F,G][A, B, D, E, C, F, G],中序遍历为 [D,B,E,A,F,C,G][D, B, E, A, F, C, G],请问该二叉树的后序遍历结果是?()

A.
[D, E, B, F, G, C, A]
B.
[D, E, B, F, G, A, C]
C.
[D, B, E, F, G, C, A]
D.
[D, E, B, F, G, A, C]

正确答案A

解析详情
答案 A由前序和中序确定二叉树遍历
思路与推导
  1. 前序首元素 A 是根;中序中 A 左侧构成以 B 为根、D 和 E 为左右孩子的子树,右侧构成以 C 为根、F 和 G 为左右孩子的子树。

  2. 按“左子树、右子树、根”的后序顺序得到 [D,E,B,F,G,C,A][D,E,B,F,G,C,A]。

第 13 题(单选题,2 分)

给定一个空栈,支持入栈和出栈操作。若入栈操作的元素依次是 1 2 3 4 5 6,其中 1 最先入栈、6 最后入栈,下面哪种出栈顺序是不可能的?()

A.
6 5 4 3 2 1
B.
1 6 5 4 3 2
C.
2 4 6 5 3 1
D.
1 3 5 2 4 6

正确答案D

解析详情
答案 D栈的后进先出性质
思路与推导
  1. 按 D 输出 1、3、5 后,栈中从底到顶为 2、4,此时若要输出 2,必须先弹出压在它上面的 4,因此该序列不可能。

  2. 其余序列都能通过适时入栈和出栈实现。

第 14 题(单选题,2 分)

有 5 个男生和 3 个女生站成一排,规定 3 个女生必须相邻。问有多少种不同的排列方式?

( )

A.
4320 种
B.
5040 种
C.
3600 种
D.
2880 种

正确答案A

解析详情
答案 A捆绑法排列
思路与推导
  1. 把 3 个女生视为一个整体,与 5 个男生共 6 个对象,可排列 6!6! 种;女生内部还有 3!3! 种排列。

  2. 总数为 6!×3!=720×6=43206!\times3!=720\times6=4320。

第 15 题(单选题,2 分)

编译器的主要作用是什么?()

A.
直接执行源代码
B.
将源代码转换为机器代码
C.
进行代码调试
D.
管理程序运行时的内存

正确答案B

解析详情
答案 B编译器的作用
思路与推导
  1. 编译器分析并翻译高级语言源代码,生成计算机可执行的目标代码或机器代码;它本身不等同于直接执行、调试或运行时内存管理。

二、阅读程序(1)

 1 | #include <iostream>
 2 | using namespace std;
 3 |
 4 | bool isPrime(int n) {
 5 |     if (n <= 1) {
 6 |         return false;
 7 |     }
 8 |     for (int i = 2; i * i <= n; i++) {
 9 |         if (n % i == 0) {
10 |             return false;
11 |         }
12 |     }
13 |     return true;
14 | }
15 |
16 | int countPrimes(int n) {
17 |     int count = 0;
18 |     for (int i = 2; i <= n; i++) {
19 |         if (isPrime(i)) {
20 |             count++;
21 |         }
22 |     }
23 |     return count;
24 | }
25 |
26 | int sumPrimes(int n) {
27 |     int sum = 0;
28 |     for (int i = 2; i <= n; i++) {
29 |         if (isPrime(i)) {
30 |             sum += i;
31 |         }
32 |     }
33 |     return sum;
34 | }
35 |
36 | int main() {
37 |     int x;
38 |     cin >> x;
39 |     cout << countPrimes(x) << " " << sumPrimes(x) << endl;
40 |     return 0;
41 | }

第 16 题(判断题,1.5 分)

当输入为 “10” 时,程序的第一个输出为 “4”,第二个输出为 “17”。()

正确答案正确

解析详情
答案 正确素数判断与程序跟踪
思路与推导
  1. 10 以内的素数为 2、3、5、7,共 4 个,元素之和为 2+3+5+7=172+3+5+7=17,所以程序输出“4 17”。

第 17 题(判断题,1.5 分)

若将 isPrime(i) 函数中的条件改为 i <= n / 2,输入 “20” 时,countPrimes(20) 的输出将变为 “6”。()

正确答案错误

解析详情
答案 错误素数判定的试除范围
思路与推导
  1. 把上界扩大为 i≤n/2i\le n/2 虽然增加了试除次数,但合数仍能找到因子、素数仍不会找到因子。20 以内仍有 8 个素数,countPrimes(20) 不会变为 6。

第 18 题(判断题,1.5 分)

sumPrimes 函数计算的是从 2 到 n 之间的所有素数之和。

正确答案正确

解析详情
答案 正确循环累加与函数语义
思路与推导
  1. sumPrimes 遍历 i=2i=2 到 nn,仅当 isPrime(i) 为真时执行 `sum += i`,因此累加的正是该范围内所有素数。

第 19 题(单选题,3 分)

当输入为 “50” 时,sumPrimes(50) 的输出为()。

A.
1060
B.
328
C.
381
D.
275

正确答案B

解析详情
答案 B素数枚举与累加
思路与推导
  1. 50 以内素数为 2、3、5、7、11、13、17、19、23、29、31、37、41、43、47。

  2. 依次相加得到 328,因此 sumPrimes(50) 返回 328。

第 20 题(单选题,3 分)

如果将 for (int i = 2; i * i <= n; i++) 改为 for (int i = 2; i <= n; i++)

输入 “10” 时,程序的输出()。

A.
将不能正确计算 10 以内素数个数及其和
B.
仍然输出 “4” 和 “17”
C.
输出 “3” 和 “10”
D.
输出结果不变,但运行时间更短

正确答案A

解析详情
答案 A素数判定循环边界
思路与推导
  1. 循环若执行到 `i == n`,任何 n≥2n\ge2 都会因 `n % n == 0` 被判为非素数。

  2. 因此 2 到 10 的数全部无法通过 isPrime,程序不能正确得到原来的素数个数与和。

二、阅读程序(2)

 1 | #include <iostream>
 2 | #include <vector>
 3 | using namespace std;
 4 |
 5 | int compute(vector<int>& cost) {
 6 |     int n = cost.size();
 7 |     vector<int> dp(n + 1, 0);
 8 |     dp[1] = cost[0];
 9 |     for (int i = 2; i <= n; i++) {
10 |         dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i - 1];
11 |     }
12 |     return min(dp[n], dp[n - 1]);
13 | }
14 |
15 | int main() {
16 |     int n;
17 |     cin >> n;
18 |     vector<int> cost(n);
19 |     for (int i = 0; i < n; i++) {
20 |         cin >> cost[i];
21 |     }
22 |     cout << compute(cost) << endl;
23 |     return 0;
24 | }

第 21 题(判断题,1.5 分)

当输入的 cost 数组为 {10,15,20}\{10, 15, 20\} 时,程序的输出为 15。()

正确答案正确

解析详情
答案 正确动态规划状态转移
思路与推导
  1. 初值为 dp[0]=0,dp[1]=10dp[0]=0,dp[1]=10,随后 dp[2]=15dp[2]=15、dp[3]=30dp[3]=30。

  2. 函数返回 min⁡(dp[3],dp[2])=15\min(dp[3],dp[2])=15。

第 22 题(判断题,1.5 分)

如果将 dp[i-1] 改为 dp[i-3],程序可能会产生编译错误。()

正确答案错误

解析详情
答案 错误数组越界与编译、运行错误
思路与推导
  1. 代码语法和类型仍然合法,通常可以通过编译;但循环第一次取 `dp[i-3]` 时 i=2i=2,访问的是 `dp[-1]`,会造成越界访问和未定义行为。

第 23 题(判断题,2 分)

程序总是输出 cost 数组中最小的元素。()

正确答案错误

解析详情
答案 错误动态规划结果的含义
思路与推导
  1. 程序根据相邻状态计算到达各位置的累计最小花费,而不是直接求数组最小值。例如 cost 为 {10,15,20}\{10,15,20\} 时输出 15,但数组最小元素是 10。

第 24 题(单选题,3 分)

当输入的 cost 数组为 {1,100,1,1,1,100,1,1,100,1}\{1, 100, 1, 1, 1, 100, 1, 1, 100, 1\} 时,程序的输出为()。

A.
"6"
B.
"7"
C.
"8"
D.
"9"

正确答案A

解析详情
答案 A动态规划递推计算
思路与推导
  1. 由 dp[i]=min⁡(dp[i−1],dp[i−2])+cost[i−1]dp[i]=\min(dp[i-1],dp[i-2])+cost[i-1] 依次得到 dp[1..10]=1,100,2,3,3,103,4,5,104,6dp[1..10]=1,100,2,3,3,103,4,5,104,6。

  2. 最终返回 min⁡(dp[10],dp[9])=min⁡(6,104)=6\min(dp[10],dp[9])=\min(6,104)=6。

第 25 题(单选题,4 分)

如果输入的 cost 数组为 {10,15,30,5,5,10,20}\{10, 15, 30, 5, 5, 10, 20\},程序的输出为()

A.
"25"
B.
"30"
C.
"35"
D.
"40"

正确答案B

解析详情
答案 B动态规划递推计算
思路与推导
  1. 初值 dp[0]=0,dp[1]=10dp[0]=0,dp[1]=10,继续递推得到 dp[2..7]=15,40,20,25,30,45dp[2..7]=15,40,20,25,30,45。

  2. 函数返回 min⁡(dp[7],dp[6])=min⁡(45,30)=30\min(dp[7],dp[6])=\min(45,30)=30。

第 26 题(单选题,3 分)

若将代码中的 min(dp[i-1], dp[i-2]) + cost[i-1] 修改为 dp[i-1] + cost[i-2], 输入 cost 数组为 {5, 10, 15} 时,程序的输出为()。

A.
"10"
B.
"15"
C.
"20"
D.
"25"

正确答案A

解析详情
答案 A修改后的递推式跟踪
思路与推导
  1. 修改后 dp[i]=dp[i−1]+cost[i−2]dp[i]=dp[i-1]+cost[i-2]。对 {5,10,15}\{5,10,15\},有 dp[1]=5dp[1]=5、dp[2]=10dp[2]=10、dp[3]=20dp[3]=20,最终返回 min⁡(20,10)=10\min(20,10)=10。

二、阅读程序(3)

 1 | #include <iostream>
 2 | #include <cmath>
 3 | using namespace std;
 4 |
 5 | int customFunction(int a, int b) {
 6 |     if (b == 0) {
 7 |         return a;
 8 |     }
 9 |     return a + customFunction(a, b-1);
10 | }
11 |
12 | int main() {
13 |     int x, y;
14 |     cin >> x >> y;
15 |     int result = customFunction(x, y);
16 |     cout << pow(result, 2) << endl;
17 |     return 0;
18 | }

第 27 题(判断题,1.5 分)

当输入为 “2 3” 时,customFunction(2, 3) 的返回值为 “64”。()

正确答案错误

解析详情
答案 错误递归函数返回值
思路与推导
  1. customFunction(2,3) 会把 2 累加 4 次,返回 2+2+2+2=82+2+2+2=8;64 是 main 中对返回值平方后的最终输出,不是函数返回值。

第 28 题(判断题,1.5 分)

当 b 为负数时,customFunction(a, b) 会陷入无限递归。()

正确答案正确

解析详情
答案 正确递归终止条件
思路与推导
  1. 递归只在 `b == 0` 时终止;若 b 初始为负数,每次执行 `b-1` 会离 0 越来越远,递归无法正常结束并最终导致栈耗尽。

第 29 题(判断题,1.5 分)

当 b 的值越大,程序的运行时间越长。()

正确答案正确

解析详情
答案 正确递归时间复杂度
思路与推导
  1. 对非负 b,每层递归把 b 减 1,直到 0,共进行与 b 成正比的调用,时间复杂度为 O(b)O(b);b 越大,运行时间通常越长。

第 30 题(单选题,3 分)

当输入为 “5 4” 时,customFunction(5, 4) 的返回值为()。

A.
5
B.
25
C.
250
D.
625

正确答案B

解析详情
答案 B递归展开与求值
思路与推导
  1. customFunction(a,b) 对非负 b 返回 (b+1)a(b+1)a。因此 customFunction(5,4) 返回 (4+1)×5=25(4+1)\times5=25。

第 31 题(单选题,3 分)

如果输入 x=3 和 y=3,则程序的最终输出为()。

A.
"27"
B.
"81"
C.
"144"
D.
"256"

正确答案C

解析详情
答案 C递归返回值与幂运算
思路与推导
  1. customFunction(3,3) 返回 (3+1)×3=12(3+1)\times3=12,main 随后计算 `pow(12, 2)`,最终输出 144。

第 32 题(单选题,4 分)

若将customFunction函数改为“return a + customFunction(a-1, b-1);”,并输入“3 3”,则程序的最终输出为()。

A.
9
B.
16
C.
25
D.
36

正确答案D

解析详情
答案 D双参数递归与程序跟踪
思路与推导
  1. 修改后 customFunction(3,3) 展开为 3+2+1+0=63+2+1+0=6,其中基例 customFunction(0,0) 返回 0。

  2. main 输出 62=366^2=36。

三、完善程序(1)判断平方数

问题:给定一个正整数 n,希望判断这个数是否为完全平方数,即存在一个正整数 x 使得 x 的平方为 n。

试补全程序。

#include<iostream>
#include<vector>
using namespace std;

bool isSquare(int num) {
    int i = ①;
    int bound = ②;
    for (; i <= bound; ++i) {
        if (③) {
            return ④;
        }
    }
    return ⑤;
}

int main() {
    int n;
    cin >> n;
    if (isSquare(n)) {
        cout << n << " is a square number" << endl;
    } else {
        cout << n << " is not a square number" << endl;
    }
    return 0;
}

第 33 题(单选题,3 分)

①处应填()

A.
1
B.
2
C.
3
D.
4

正确答案A

解析详情
答案 A完全平方数的枚举起点
思路与推导
  1. 题目要求寻找正整数 x,最小候选值是 1,因此循环变量 i 应从 1 开始,才能覆盖 121^2。

第 34 题(单选题,3 分)

②处应填()

A.
(int)floor(sqrt(num))-1
B.
(int)floor(sqrt(num))
C.
floor(sqrt(num/2))-1
D.
floor(sqrt(num/2))

正确答案B

解析详情
答案 B平方根与枚举上界
思路与推导
  1. 若 num 是完全平方数,它的正整数平方根必为 num\sqrt{num};枚举到 ⌊num⌋\lfloor\sqrt{num}\rfloor 即可覆盖唯一可能的整数根,同时无需检查更大的 i。

第 35 题(单选题,3 分)

③处应填()

A.
num = 2 * i
B.
num == 2 * i
C.
num = i * i
D.
num == i * i

正确答案D

解析详情
答案 D完全平方数的判定条件
思路与推导
  1. 完全平方数的定义是存在整数 i 使 i×i=numi\times i=num,因此条件应写为 `num == i * i`。

第 36 题(单选题,3 分)

④处应填()

A.
num = 2 * i
B.
num == 2 * i
C.
true
D.
false

正确答案C(另接受:A)

解析详情
答案 C布尔函数返回值
思路与推导
  1. 进入该分支说明已经满足 `num == i * i`,即确认 num 是完全平方数,所以 isSquare 应立即返回 true。

第 37 题(单选题,3 分)

⑤处应填( )

A.
num = i * i
B.
num != i * i
C.
true
D.
false

正确答案D

解析详情
答案 D枚举结束后的布尔返回值
思路与推导
  1. 循环检查完 11 到 ⌊num⌋\lfloor\sqrt{num}\rfloor 仍未命中 `num == i * i`,说明不存在整数平方根,应返回 false。

三、完善程序(2)汉诺塔问题

给定三根柱子,分别标记为 A、B 和 C。初始状态下,柱子 A 上有若干个圆盘,这些圆盘从上到下按从小到大的顺序排列。任务是将这些圆盘全部移到柱子 C 上,且必须保持原有顺序不变。在移动过程中,需要遵守以下规则:

1. 只能从一根柱子的顶部取出圆盘,并将其放入另一根柱子的顶部。

2. 每次只能移动一个圆盘。

3. 小圆盘必须始终在大圆盘之上。

试补全程序。

#include <iostream>
#include <vector>
using namespace std;

void move(char src, char tgt) {
    cout << "从柱子" << src << "挪到柱子" << tgt << endl;
}

void dfs(int i, char src, char tmp, char tgt) {
    if (i == ①) {
        move(②);
        return;
    }
    dfs(i - 1, ③);
    move(src, tgt);
    dfs(⑤, ④);
}

int main() {
    int n;
    cin >> n;
    dfs(n, 'A', 'B', 'C');
}

第 38 题(单选题,3 分)

①处应填()

A.
0
B.
1
C.
2
D.
3

正确答案B

解析详情
答案 B汉诺塔递归基
思路与推导
  1. 当只剩 1 个圆盘时,可以直接从 src 移到 tgt,无需继续分解,因此递归终止条件应为 `i == 1`。

第 39 题(单选题,3 分)

②处应填()

A.
src, tmp
B.
src, tgt
C.
tmp, tgt
D.
tgt, tmp

正确答案B

解析详情
答案 B汉诺塔基本移动
思路与推导
  1. 在 `i == 1` 的基例中,唯一的圆盘应直接从源柱 src 移到目标柱 tgt,因此调用 `move(src, tgt)`。

第 40 题(单选题,3 分)

③处应填()

A.
src, tmp, tgt
B.
src, tgt, tmp
C.
tgt, tmp, src
D.
tgt, src, tmp

正确答案B

解析详情
答案 B汉诺塔递归参数映射
思路与推导
  1. 移动最大圆盘前,要先把上方 i−1i-1 个圆盘从 src 移到 tmp,此时原 tgt 充当辅助柱。

  2. 按函数参数 `(src, tmp, tgt)` 的角色传入应为 `src, tgt, tmp`。

第 41 题(单选题,3 分)

④处应填()

A.
src, tmp, tgt
B.
tmp, src, tgt
C.
src, tgt, tmp
D.
tgt, src, tmp

正确答案B

解析详情
答案 B汉诺塔递归参数映射
思路与推导
  1. 最大圆盘移到 tgt 后,要把暂存在 tmp 的 i−1i-1 个圆盘移到 tgt,此时 src 充当辅助柱。

  2. 因此三根柱子参数应依次为 `tmp, src, tgt`。

第 42 题(单选题,3 分)

⑤处应填()

A.
0
B.
1
C.
i - 1
D.
i

正确答案C

解析详情
答案 C汉诺塔递归规模缩减
思路与推导
  1. 移动最大圆盘后,tmp 上还剩 i−1i-1 个较小圆盘需要递归移到 tgt,所以调用规模应为 `i - 1`。