























2024 年 CSP-J(入门级)第一轮认证于 2024 年 9 月 21 日举行。继上一篇单项选择题解析后,本文为您带来第二部分:阅读程序题(共 3 大题,计 40 分)的逐题源码分析、逻辑推理与深度全解析。
阅读程序题主要考查对 C++ 语法细节、基础算法逻辑(素数判定、动态规划、递归思想)及代码执行轨迹跟踪能力。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
using namespace std;
bool isPrime(int n) {
if (n <= 1) {
return false;
}
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
int countPrimes(int n) {
int count = 0;
for (int i = 2; i <= n; i++) {
if (isPrime(i)) {
count++;
}
}
return count;
}
int sumPrimes(int n) {
int sum = 0;
for (int i = 2; i <= n; i++) {
if (isPrime(i)) {
sum += i;
}
}
return sum;
}
int main() {
int x;
cin >> x;
cout << countPrimes(x) << " " << sumPrimes(x) << endl;
return 0;
}
本程序包含三个主要函数:
isPrime(n):用试除法判断整数 $n$ 是否为素数(试除范围到 $\sqrt{n}$)。countPrimes(n):统计从 $2$ 到 $n$ 之间(包含 $n$)所有素数的个数。sumPrimes(n):计算从 $2$ 到 $n$ 之间(包含 $n$)所有素数数值的总和。A. 正确
B. 错误
正确答案: A
深度解析: 当输入 $x = 10$ 时:
countPrimes(10) 返回 4。sumPrimes(10) 返回 17。4 17,与题干描述完全相符。故本题正确。isPrime(i) 函数中的条件改为 i<=n/2,输入 20 时,countPrimes(20) 的输出将变为 6。( )A. 正确
B. 错误
正确答案: B
深度解析: 在 isPrime(int n) 函数中,若将循环条件从 i * i <= n 改为 i <= n / 2:
countPrimes(20) 依然能够正确统计出 $20$ 以内的所有素数:$2, 3, 5, 7, 11, 13, 17, 19$,共 8 个素数,输出依然为 8,而非 6。故本题错误。sumPrimes 函数计算的是从 2 到 $n$ 之间的所有素数之和。( )A. 正确
B. 错误
正确答案: A
深度解析: 观察 sumPrimes(int n) 的实现:循环变量 i 从 2 增加到 n,当 isPrime(i) 为 true 时累加 sum += i,返回值即为 $[2, n]$ 闭区间内所有素数之和。描述完全准确,故本题正确。
sumPrimes(50) 的输出为( )。A. 1060
B. 328
C. 381
D. 275
正确答案: B
深度解析: 计算 $50$ 以内的所有素数之和:
总和求计算: \(77 + 52 + 68 + 131 = \mathbf{328}\)
故正确答案为 B。
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
深度解析: 若将 isPrime(int n) 中的循环条件改为 i <= n:
i 会一直递增到等于 $n$。i == n 时,必定触发 n % i == 0,函数直接返回 false。countPrimes(10) 将返回 0,sumPrimes(10) 将返回 0,程序无法正确计算素数个数及和。故正确答案为 A。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <vector>
using namespace std;
int compute(vector<int>& cost) {
int n = cost.size();
vector<int> dp(n + 1, 0);
dp[1] = cost[0];
for (int i = 2; i <= n; i++) {
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i - 1];
}
return min(dp[n], dp[n - 1]);
}
int main() {
int n;
cin >> n;
vector<int> cost(n);
for (int i = 0; i < n; i++) {
cin >> cost[i];
}
cout << compute(cost) << endl;
return 0;
}
本题模型来源于经典的动态规划问题——最小花费爬楼梯(Min Cost Climbing Stairs)。
cost[i] 表示踏上第 $i$ 阶台阶所需的费用。dp[i] 表示踏上第 $i$ 阶台阶(1-based 索引,对应 cost[i-1])所需的累计最小费用。min(dp[n], dp[n-1])。cost 数组为 {10, 15, 20} 时,程序的输出为 15。( )A. 正确
B. 错误
正确答案: A
深度解析: 逐步追踪 compute 函数:
n = 3,cost = [10, 15, 20],初始化 dp[0] = 0, dp[1] = cost[0] = 10。dp[2] = min(dp[1], dp[0]) + cost[1] = min(10, 0) + 15 = 15dp[3] = min(dp[2], dp[1]) + cost[2] = min(15, 10) + 20 = 30min(dp[3], dp[2]) = min(30, 15) = 15。输出确实为 15,故本题正确。
dp[i-1] 改为 dp[i-3],程序可能会产生编译错误。( )A. 正确
B. 错误
正确答案: B
深度解析:
dp[i-1] 改为 dp[i-3] 后,当 $i = 2$ 时,dp[i-3] 即为 dp[-1]。std::vector 的 [] 运算符不进行边界检查,下标 -1 属于越界内存访问(Undefined Behavior / 运行期段错误 Segmentation Fault)。cost 数组中最小的元素。( )A. 正确
B. 错误
正确答案: B
深度解析: 程序求解的是路径上所有选中台阶的花费总和的最小值,而不是找数组元素的最小值。例如,当 cost = [10, 15, 20] 时,输出为 15,而数组中最小的元素是 10。反例明显,故本题错误。
cost 数组为 {1, 100, 1, 1, 100, 1, 1, 100, 1} 时,程序的输出为( )。A. 6
B. 7
C. 8
D. 9
正确答案: A
深度解析: 数组 cost = [1, 100, 1, 1, 100, 1, 1, 100, 1],长度 $n = 9$。
填表计算 dp[i]:
| $i$ | cost[i-1] | dp[i] 计算公式 min(dp[i-1], dp[i-2]) + cost[i-1] | dp[i] |
|---|---|---|---|
| 0 | - | 初始化 | 0 |
| 1 | 1 | 初始化 cost[0] | 1 |
| 2 | 100 | $\min(1, 0) + 100$ | 100 |
| 3 | 1 | $\min(100, 1) + 1$ | 2 |
| 4 | 1 | $\min(2, 100) + 1$ | 3 |
| 5 | 100 | $\min(3, 2) + 100$ | 102 |
| 6 | 1 | $\min(102, 3) + 1$ | 4 |
| 7 | 1 | $\min(4, 102) + 1$ | 5 |
| 8 | 100 | $\min(5, 4) + 100$ | 104 |
| 9 | 1 | $\min(104, 5) + 1$ | 6 |
最终返回 min(dp[9], dp[8]) = min(6, 104) = 6。故正确答案为 A。
cost 数组为 {10, 15, 30, 5, 5, 10, 20},程序的输出为( )。A. 25
B. 30
C. 35
D. 40
正确答案: B
深度解析: 数组 cost = [10, 15, 30, 5, 5, 10, 20],长度 $n = 7$。
填表计算 dp[i]:
| $i$ | cost[i-1] | dp[i] 计算公式 | dp[i] |
|---|---|---|---|
| 0 | - | - | 0 |
| 1 | 10 | cost[0] | 10 |
| 2 | 15 | $\min(10, 0) + 15$ | 15 |
| 3 | 30 | $\min(15, 10) + 30$ | 40 |
| 4 | 5 | $\min(40, 15) + 5$ | 20 |
| 5 | 5 | $\min(20, 40) + 5$ | 25 |
| 6 | 10 | $\min(25, 20) + 10$ | 30 |
| 7 | 20 | $\min(30, 25) + 20$ | 45 |
最终返回 min(dp[7], dp[6]) = min(45, 30) = 30。故正确答案为 B。
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
深度解析: 修改后的状态转移方程为:dp[i] = dp[i-1] + cost[i-2]。 对于 cost = [5, 10, 15]($n = 3$):
dp[0] = 0dp[1] = cost[0] = 5dp[2] = dp[1] + cost[0] = 5 + 5 = 10dp[3] = dp[2] + cost[1] = 10 + 10 = 20min(dp[3], dp[2]) = min(20, 10) = 10。故正确答案为 A。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <cmath>
using namespace std;
int customFunction(int a, int b) {
if (b == 0) {
return a;
}
return a + customFunction(a, b - 1);
}
int main() {
int x, y;
cin >> x >> y;
int result = customFunction(x, y);
cout << pow(result, 2) << endl;
return 0;
}
customFunction(a, b) 是一个递归函数: \(customFunction(a, b) = a + a + \dots + a \quad (\text{共 } b+1 \text{ 项}) = a \times (b + 1)\)main 函数读取 $x, y$,计算 $result = customFunction(x, y) = x \times (y + 1)$,最后输出 $result^2$。2 3 时,customFunction(2, 3) 的返回值为 64。( )A. 正确
B. 错误
正确答案: B
深度解析:
customFunction(2, 3) 的返回值,而不是主函数的输出!customFunction(2, 3) = 2 * (3 + 1) = 8,返回值为 8。pow(8, 2) = 64。customFunction(a, b) 会陷入无限递归。( )A. 正确
B. 错误
正确答案: A / B (官方认定本题题目表述存在瑕疵,选择【正确】或【错误】均能获得对应分数)
深度解析:
b == 0,在理论逻辑上确实会陷入无限递归(导致栈溢出 Stack Overflow 异常崩溃)。A. 正确
B. 错误
正确答案: A / B (官方认定本题题目表述存在瑕疵,选择【正确】或【错误】均能获得对应分数)
深度解析:
5 4 时,customFunction(5, 4) 的返回值为( )。A. 5
B. 25
C. 250
D. 625
正确答案: B
深度解析:
customFunction(5, 4) 的返回值: \(customFunction(5, 4) = 5 \times (4 + 1) = \mathbf{25}\)x=3 和 y=3,则程序的最终输出为( )。A. 27
B. 81
C. 144
D. 256
正确答案: C
深度解析:
result = customFunction(3, 3) = 3 * (3 + 1) = 12。pow(12, 2) = 12^2 = 144。customFunction 函数改为 return a + customFunction(a - 1, b - 1);,并输入 3 3,则程序的最终输出为( )。A. 9
B. 16
C. 25
D. 36
正确答案: D
深度解析: 修改后的递归展开:
1
2
3
4
5
6
customFunction(3, 3)
= 3 + customFunction(2, 2)
= 3 + 2 + customFunction(1, 1)
= 3 + 2 + 1 + customFunction(0, 0)
= 3 + 2 + 1 + 0 // 当 b == 0 时,返回第一个参数 a = 0
= 6
主函数最终输出 pow(6, 2) = 6^2 = 36。故正确答案为 D。
[!TIP] 本篇结语 以上为 CSP-J 2024 第一轮认证(初赛)阅读程序题 3 大题的全解析。在本篇中,我们解析了素数判定、动态规划和递归推论等核心题型。 本系列的第三篇将为您带来最后一项重头戏——完善程序题的解析,敬请期待!
所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code
GESP 学习专题站:GESP WIKI
"luogu-"系列题目可在洛谷题库进行在线评测。
"bcqm-"系列题目可在编程启蒙题库进行在线评测。
欢迎加入:Java、C++、Python技术交流QQ群(982860385),大佬免费带队,有问必答
欢迎加入:C++ GESP/CSP认证学习QQ频道,考试资源总结汇总
欢迎加入:C++ GESP/CSP学习交流QQ群(688906745),考试认证学员交流,互帮互助
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。