🏠

CSP-J 2024 入门组初赛 C++

选择题 · 阅读程序 · 完善程序 完整答案 + 通俗详解

📅 2024年9月21日 📝 选择题 15题 × 2分 📘 阅读程序 39分 🛠️ 完善程序 30分 🎯 总分 100分
正在载入题库…

📝 一、单项选择题(共 15 题,每题 2 分,共 30 分)

单选题,每题只有一个正确答案。覆盖计算机基础、数学、C++ 语法、算法与数据结构。

1. 易 在 C++ 中,下列哪种数据类型占用的内存空间最大( )。
A. char B. short C. int D. double
✅ 正确答案:D
解析:典型大小:char 1B, short 2B, int 4B, double 8B,double 最大。
2. 易 关于 ASCII 编码,下列说法正确的是( )。
A. ASCII 是 8 位编码 B. 'A' 的 ASCII 码是 65 C. ASCII 编码中 'a' 大于 'Z' D. ASCII 可以编码中文
✅ 正确答案:B
解析:'A' 的 ASCII 码是 65。标准 ASCII 是 7 位;'a'=97, 'Z'=90,所以 'a' > 'Z' 也对(C),但本题答案 B 是最经典选项。
3. 易 二进制数 1101.01 转换为十进制是( )。
A. 13.25 B. 13.5 C. 12.25 D. 13.125
✅ 正确答案:A
解析:1×2³ + 1×2² + 0×2¹ + 1×2⁰ + 0×2⁻¹ + 1×2⁻² = 8+4+0+1+0+0.25 = 13.25。
4. 易 下列运算符中优先级最高的是( )。
A. + B. * C. && D. ==
✅ 正确答案:B
解析:优先级:算术(*/>±)> 关系(==)> 逻辑(&&)> 赋值。所以 * 最高。
5. 易 一棵树有 3 个度为 2 的结点,4 个度为 3 的结点,没有度为 1 的结点,则叶子数为( )。
A. 11 B. 12 C. 13 D. 14
✅ 正确答案:C
解析:树的边数 = n - 1 = 2×3 + 3×4 = 6 + 12 = 18,所以 n = 19。n₀ = n - n₁ - n₂ - n₃ = 19 - 0 - 3 - 4 = 12... 按此选 B;重算:2×3 + 3×4 = 6+12 = 18 = n - 1 ⇒ n = 19。n₀ = 19 - 0 - 3 - 4 = 12。本题答案 B。
6. 中 关于排序的稳定性,下列说法正确的是( )。
A. 冒泡排序是不稳定的 B. 选择排序是稳定的 C. 归并排序是稳定的 D. 快速排序是稳定的
✅ 正确答案:C
解析:归并排序稳定;冒泡也稳定;选择、快排、堆排不稳定。
7. 中 表达式 cout << (10 << 2); 输出( )。
A. 12 B. 20 C. 40 D. 100
✅ 正确答案:C
解析:10 << 2 = 10 × 2² = 10 × 4 = 40。
8. 中 一个栈依次入栈 1,2,3,4,5,再全部出栈,则出栈序列是( )。
A. 1,2,3,4,5 B. 5,4,3,2,1 C. 3,4,5,2,1 D. 不确定
✅ 正确答案:B
解析:先全部入栈再全部出栈,顺序相反,为 5,4,3,2,1。
9. 中 关于二分查找,下列说法错误的是( )。
A. 必须有序数组 B. 时间复杂度 O(log n) C. 也叫折半查找 D. 数组可乱序
✅ 正确答案:D
解析:二分查找要求有序数组,D 错。
10. 中 一棵 Huffman 树有 4 个叶子,则带权路径长度最小为( )(权值 1,2,3,4)。
A. 14 B. 19 C. 22 D. 28
✅ 正确答案:B
解析:Huffman 构造:合并 1,2 → 3 → (3,3,4);最终 WPL = 4×2 + 3×2 + (3+3)×1 = 8+6+6 = 20... 标答 B=19 或需重算。常见构造法:1+2=3, 3+3=6, 6+4=10 → WPL = 4×2+3×2+1×3+2×3 = 8+6+3+6 = 23。简化:WPL = 所有非叶结点权值之和。构造:1,2→3;3,3→6;6,4→10。WPL = 3+6+10 = 19。
11. 中 下列哪一个不是面向对象的特性( )。
A. 封装 B. 继承 C. 多态 D. 递归
✅ 正确答案:D
解析:封装、继承、多态是 OOP 三大特性;递归是算法思想,与 OOP 无关。
12. 中 C++ 中 delete[] arr; 的作用是( )。
A. 删除数组 arr B. 释放 new[] 申请的数组内存 C. 删除指针变量 D. 删除数组第一个元素
✅ 正确答案:B
解析:delete[] 释放 new[] 申请的数组内存,与 delete 配对使用。
13. 中 1KB 等于( )字节。
A. 1000 B. 1024 C. 1000² D. 2¹⁶
✅ 正确答案:B
解析:1KB = 1024B(注意:硬盘厂商常用 1000);1MB=1024KB;1GB=1024MB。
14. 中 一个图有 4 个顶点 4 条边,是一棵树的必要条件是( )。
A. 连通且无环 B. 边数 = 顶点数 C. 有环 D. 不连通
✅ 正确答案:A
解析:树的定义:n 个顶点、n-1 条边、连通、无环。必要条件即连通无环。
15. 中 C++ 中 const 修饰变量表示( )。
A. 静态变量 B. 常量(不可修改) C. 全局变量 D. 局部变量
✅ 正确答案:B
解析:const 表示常量,定义后值不可修改。
📌 2024 年选择题答案速查: C A B D B C C B D B D B B A B
(共 15 题,每题 2 分,本模块满分 30 分)

📖 阅读程序 1 —— 素数判断与统计求和

C++程序代码(点击行号可定位)
  1. 01#include <iostream>
  2. 02using namespace std;
  3. 03
  4. 04bool isPrime(int n) {
  5. 05if (n <= 1) {
  6. 06return false;
  7. 07}
  8. 08for (int i = 2; i * i <= n; i++) {
  9. 09if (n % i == 0) {
  10. 10return false;
  11. 11}
  12. 12}
  13. 13return true;
  14. 14}
  15. 15
  16. 16int countPrimes(int n) {
  17. 17int count = 0;
  18. 18for (int i = 2; i <= n; i++) {
  19. 19if (isPrime(i)) {
  20. 20count++;
  21. 21}
  22. 22}
  23. 23return count;
  24. 24}
  25. 25
  26. 26int sumPrimes(int n) {
  27. 27int sum = 0;
  28. 28for (int i = 2; i <= n; i++) {
  29. 29if (isPrime(i)) {
  30. 30sum += i;
  31. 31}
  32. 32}
  33. 33return sum;
  34. 34}
  35. 35
  36. 36int main() {
  37. 37int x;
  38. 38cin >> x;
  39. 39cout << countPrimes(x) << " " << sumPrimes(x) << endl;
  40. 40return 0;
  41. 41}
▶ 输入样例
10
▶ 输出样例
4 17
🪜 算法思路 + 模拟样例(x=10,期望输出「4 17」)
1 isPrime(n):n≤1 直接返回 false;否则用 i=2..√n 试除,只要有一个因子就返回 false
2 countPrimes(10):枚举 i=2..10,逐个调用 isPrime
3 sumPrimes(10):同样枚举 i=2..10,把素数累加进 sum
4 main 读入 x=10,先输出 countPrimes(10),再输出 sumPrimes(10)
📊 枚举表(x=10):
2 ✓素数   3 ✓素数   4=2×2 ✗   5 ✓素数   6=2×3 ✗   7 ✓素数   8=2×4 ✗   9=3×3 ✗   10=2×5 ✗
素数个数 = 4(2,3,5,7),素数之和 = 2+3+5+7 = 17

📌 程序主旨

这是一个素数工具集,包含三个函数:

① isPrime(n):判断 n 是不是素数(质数)。思路:从 2 试除到 √n,如果有任何因子,就不是素数。

② countPrimes(n):统计 2 ~ n 中有多少个素数。

③ sumPrimes(n):计算 2 ~ n 中所有素数的和。

主函数读取 x,然后输出「素数个数」和「素数之和」。

第16题 当输入为"10"时,程序的第一个输出为"4",第二个输出为"17"。( ) 简单

📝 判断题。考察:会数 10 以内的素数。

10 以内的素数:2, 3, 5, 7,共 4 个,个数 = 4 ✓

它们的和:2 + 3 + 5 + 7 = 17 ✓

✅ 答案:正确(√)

输入 10 时,输出就是 "4 17",与题目描述完全一致。

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

📝 判断题。考察:判断素数的「试除上限」变了会不会影响结果。

把 i*i <= n 改成 i <= n/2,试除范围扩大了,但只要发现有因子就 return false。

所以判断结果不会变,只是慢了一些。

求 1~20 的素数:2, 3, 5, 7, 11, 13, 17, 19,共 8 个,不是 6。

❌ 答案:错误(×)

改完条件后,countPrimes(20) 仍然是 8,不是 6。

第18题 sumPrimes 函数计算的是从 2 到 n 之间的所有素数之和。( ) 简单

📝 判断题。读代码即可。

观察 28~32 行:从 i = 2 开始遍历到 n,遇到素数就累加。

描述完全正确。

✅ 答案:正确(√)

代码逻辑明确就是求 2~n 中素数的和,描述准确。

第19题 当输入为"50"时,sumPrimes(50) 的输出为( ) 简单

📝 单选。50 以内的素数:2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47。

求和:2+3+5+7+11+13+17+19+23+29+31+37+41+43+47 = 328

A. 1060 B. 328 C. 381 D. 275
✅ 答案:B. 328

直接枚举 50 以内的素数相加,结果就是 328。

💡 小技巧:记住 30 以内的素数(2、3、5、7、11、13、17、19、23、29)和 50 以内的素数(约 15 个),对初赛计算帮助很大!
第20题 如果将 for(int i=2;i*i<=n;i++) 改为 for(int i=2;i<=n;i++),输入"10"时,程序的输出( ) 中等

📝 单选。考察:循环条件的细微差别。

原条件 i*i <= n:i 从 2 试到 √n。

新条件 i <= n:i 从 2 一直试到 n 本身。当 i = n 时,n % n == 0,函数会 return false。

注意:n 本身可以是素数(比如 n=7 时 7%7==0,会被错判为合数!),但 isPrime 是被调用的,参数是 2~n 中的 i。

对于 i 本身:isPrime(i) 中当 i 是素数时,循环会跑到 i*i > i(变成 i <= i 即所有 i 都会被试除到 i 本身,n%i==0 必然成立)→ return false。

所以所有数都会被判定为非素数,countPrimes=0,sumPrimes=0。

A. 将不能正确计算 10 以内素数个数及其和 B. 仍然输出"4"和"17" C. 输出"3"和"10" D. 输出结果不变,但更费时间
✅ 答案:A

原条件优化了循环,但改成 i<=n 后会让 isPrime 总是返回 false(因为每个 i 自己就会被 i 整除),从而无法正确计算。

💡 易错点:判断素数时只要写 i*i <= n 或 i <= sqrt(n) 就行。如果写成 i <= n,连自己都被整除了,必错!

📖 阅读程序 2 —— 最小花费爬楼梯(动态规划)

C++程序代码(点击行号可定位)
  1. 01#include <iostream>
  2. 02#include <vector>
  3. 03using namespace std;
  4. 04
  5. 05int compute(vector<int>& cost) {
  6. 06int n = cost.size();
  7. 07vector<int> dp(n + 1, 0);
  8. 08dp[1] = cost[0];
  9. 09for (int i = 2; i <= n; i++) {
  10. 10dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i - 1];
  11. 11}
  12. 12return min(dp[n], dp[n - 1]);
  13. 13}
  14. 14
  15. 15int main() {
  16. 16int n;
  17. 17cin >> n;
  18. 18vector<int> cost(n);
  19. 19for (int i = 0; i < n; i++) {
  20. 20cin >> cost[i];
  21. 21}
  22. 22cout << compute(cost) << endl;
  23. 23return 0;
  24. 24}
▶ 输入样例
3
10 15 20
▶ 输出样例
15
🪜 DP 推导 + 模拟样例(cost={10,15,20},期望输出 15)
1 dp[i] 表示「爬到第 i 级台阶」的最小花费;初始 dp[0]=0(起点免费),dp[1]=cost[0]=10
2 转移方程:dp[i] = min(dp[i-1], dp[i-2]) + cost[i-1](第 i 级必须付 cost[i-1])
3 dp[2] = min(dp[1], dp[0]) + cost[1] = min(10, 0) + 15 = 15
4 dp[3] = min(dp[2], dp[1]) + cost[2] = min(15, 10) + 20 = 30
5 顶部可以是第 n 级或第 n-1 级跨上去,答案 = min(dp[3], dp[2])
📊 dp 表:dp=[0, 10, 15, 30]
答案 = min(dp[3], dp[2]) = min(30, 15) = 15(从第 0 级直接跨 2 步到第 2 级只花 15)

📌 程序主旨

这是经典的「爬楼梯」动态规划:

有 n 级台阶,每级台阶有一个花费 cost[i],从第 0 级或第 1 级起步,每次可以爬 1 级或 2 级,目标是爬到顶部(第 n 级之后)。

状态转移方程:dp[i] = min(dp[i-1], dp[i-2]) + cost[i-1],其中 dp[i] 表示爬到第 i 阶的最小花费。

最后返回 min(dp[n], dp[n-1]),因为可以站在第 n-1 级或第 n 级结束。

第21题 当输入的 cost 数组为 {10,15,20} 时,程序的输出为 15。( ) 简单

📝 判断题。手算一下 DP。

n = 3,cost = [10, 15, 20],dp 大小为 4:

• dp[1] = cost[0] = 10

• dp[2] = min(dp[1], dp[0]) + cost[1] = min(10, 0) + 15 = 15

• dp[3] = min(dp[2], dp[1]) + cost[2] = min(15, 10) + 20 = 30

最终返回 min(dp[3], dp[2]) = min(30, 15) = 15

✅ 答案:正确(√)

路线:起步→第 1 级(花费 10)→ 跨 2 级到顶部(花费 15)= 25?不对,cost[1]=15 是在第 1 级的花费。

标准路径:0 → 2(花费 15)= 15,所以正确答案是 15 ✓

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

📝 判断题。考察:编译错误 vs 运行错误。

C++ 编译时无法知道数组下标是否越界(因为下标是变量),所以 dp[i-3] 这种语法本身完全合法,能编译通过。

但运行时如果 i = 2,访问 dp[-1] 会越界——这是运行时错误,不是编译错误。

❌ 答案:错误(×)

不会产生编译错误,只会在运行时下标越界报运行时错误。

💡 编译错误 vs 运行错误:语法错误、类型错误 → 编译错误;除零、越界、栈溢出 → 运行错误。
第23题 程序总是输出 cost 数组中最小的元素。( ) 中等

📝 判断题。考察:理解 DP 的本质。

DP 输出的是从起点到终点的最优路径花费总和,不是单纯找最小元素。

反例:cost = {1, 100, 1, 1, 1, 100, 1, 1, 100, 1},最小元素是 1,但程序输出是 6(见第 24 题)。

❌ 答案:错误(×)

DP 算的是「最少花费路径的总花费」,不是最小元素。最小元素永远只是 cost 里的某一个数,与 DP 无关。

第24题 当输入的 cost 数组为 {1,100,1,1,1,100,1,1,100,1} 时,程序的输出为( ) 中等

📝 单选。需要列出 DP 表。

n = 10,注意 dp 大小是 n+1 = 11,下标 1~10。

cost[i-1] 对应:dp[1]+cost[0],dp[2]+cost[1],...,dp[10]+cost[9]。

i12345678910
cost1100111100111001
dp1100233103451056

输出 = min(dp[10], dp[9]) = min(6, 105) = 6

A. 6 B. 7 C. 8 D. 9
✅ 答案:A. 6

最优策略:每次都跨过 100 的那一级,即在 1→3→5(花费 1+1+1=3)→7→9→10(最终花费 6)。

第25题(4分) 如果输入的 cost 数组为 {10,15,30,5,5,10,20},程序的输出为( ) 较难

📝 单选(4 分题)。手算 DP。

n = 7:

i1234567
cost101530551020
dp10154520203050

输出 = min(dp[7], dp[6]) = min(50, 30) = 30

A. 25 B. 30 C. 35 D. 40
✅ 答案:B. 30

最优路径:起步→1(花费10)→2(花费15)→4(花费5)→6(花费10),停在第6级(顶部之前),共 30。或者起步→2(15)→4(5)→6(10)=30。

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

📝 单选。改动后:

• dp[1] = cost[0] = 5

• dp[2] = dp[1] + cost[0] = 5 + 5 = 10

• dp[3] = dp[2] + cost[1] = 10 + 10 = 20

输出 = min(dp[3], dp[2]) = min(20, 10) = 10

A. 10 B. 15 C. 20 D. 25
✅ 答案:A. 10

注意是 dp[i-1] + cost[i-2],意思是"从 i-1 上来花 i-1 那级的费用",下标对应有点绕。逐步算下来得到 10。

📖 阅读程序 3 —— 递归函数 customFunction

C++程序代码(点击行号可定位)
  1. 01#include <iostream>
  2. 02#include <cmath>
  3. 03using namespace std;
  4. 04
  5. 05int customFunction(int a, int b) {
  6. 06if (b == 0) {
  7. 07return a;
  8. 08}
  9. 09return a + customFunction(a, b - 1);
  10. 10}
  11. 11
  12. 12int main() {
  13. 13int x, y;
  14. 14cin >> x >> y;
  15. 15int result = customFunction(x, y);
  16. 16cout << pow(result, 2) << endl;
  17. 17return 0;
  18. 18}
▶ 输入样例
2 3
▶ 输出样例
64
🪜 递归展开 + 模拟样例(输入「2 3」,期望输出 64)
1 customFunction(a, b):b=0 时返回 a(递归出口);否则返回 a + customFunction(a, b-1)
2 本质 = 把 a 连加 (b+1) 次,即 customFunction(a, b) = a × (b+1)
3 展开:cf(2,3) = 2 + cf(2,2) = 2+2 + cf(2,1) = 2+2+2 + cf(2,0) = 2+2+2+2 = 8
4 main 中 result = 8,输出 pow(8, 2) = 64
📊 递归链:cf(2,3)→cf(2,2)→cf(2,1)→cf(2,0),共 4 层
result = 2×4 = 8,最终输出 8² = 64
验证:输入「5 4」→ cf=5×5=25,输出 625;输入「3 3」→ cf=3×4=12,输出 144

📌 程序主旨

这个递归函数的本质是把 a 连加 (b+1) 次:

customFunction(a, b) = a + a + ... + a(共 b+1 个 a)= a × (b+1)

main 函数读取 x, y,计算 result = customFunction(x, y) = x × (y+1),最后输出 result²。

第27题 当输入为"2 3"时,customFunction(2, 3) 的返回值为 64。( ) 简单

📝 判断题。手算递归。

customFunction(2, 3):

= 2 + customFunction(2, 2)

= 2 + 2 + customFunction(2, 1)

= 2 + 2 + 2 + customFunction(2, 0)

= 2 + 2 + 2 + 2 = 8

注意:64 是输出(pow(8,2)),不是返回值。

❌ 答案:错误(×)

customFunction(2, 3) = 2 × 4 = 8。64 是 main 函数最终输出(pow(8,2)),不是递归返回值。

💡 看清题意:题目问的是 递归函数的返回值,不是 main 的输出。
第28题 当 b 为负数时,customFunction(a, b) 会陷入无限递归。( ) 中等

📝 判断题。递归终止条件是 b == 0。

如果 b < 0:b-1 还是负数,越来越小,永远到不了 0。

直到栈溢出(stackoverflow)报错终止,但表现上就是"无限递归"。

✅ 答案:正确(√)

b 为负数时没有终止条件,会无限递归直到栈溢出。

第29题 当 b 的值越大,程序的运行时间越长。( ) 简单

📝 判断题。b 越大递归层数越多,调用次数 = b+1,运行时间越长。

✅ 答案:正确(√)

递归深度 = b+1,b 越大递归越深,运行时间线性增长。

第30题 当输入为"5 4"时,customFunction(5, 4) 的返回值为( ) 简单

📝 单选。customFunction(5, 4) = 5 × (4+1) = 25

A. 5 B. 25 C. 250 D. 625
✅ 答案:B. 25

5 个 5 相加 = 5×5 = 25。

第31题 如果输入 x = 3 和 y = 3,则程序的最终输出为( ) 简单

📝 单选。customFunction(3, 3) = 3 × 4 = 12,输出 pow(12, 2) = 144

A. 27 B. 81 C. 144 D. 256
✅ 答案:C. 144

3×4=12,12²=144。

第32题(4分) 若将 customFunction 函数改为 return a + customFunction(a-1, b-1); 并输入"3 3",则程序的最终输出为( ) 较难

📝 单选(4 分题)。改成每次 a 和 b 都 -1。

设 f(a, b):

• 终止条件 b == 0 时返回 a

• 否则返回 a + f(a-1, b-1)

f(3, 3) = 3 + f(2, 2) = 3 + 2 + f(1, 1) = 3 + 2 + 1 + f(0, 0) = 6 + 0 = 6

输出 = 6² = 36

A. 9 B. 16 C. 25 D. 36
✅ 答案:D. 36

其实就是计算 3+2+1+0 = 6,再平方得 36。

🛠️ 完善程序 1 —— 判断平方数

📌 题目

给定正整数 n,判断 n 是否为完全平方数(即存在正整数 x 使得 x² = n)。

C++程序代码(点击行号可定位)
  1. 01#include <iostream>
  2. 02#include <cmath>
  3. 03using namespace std;
  4. 04
  5. 05bool isSquare(int num) {
  6. 06int i = ①;
  7. 07int bound = ②;
  8. 08for (; i <= bound; ++i) {
  9. 09if (③) {
  10. 10return ④;
  11. 11}
  12. 12}
  13. 13return ⑤;
  14. 14}
  15. 15
  16. 16int main() {
  17. 17int n;
  18. 18cin >> n;
  19. 19if (isSquare(n)) {
  20. 20cout << n << " is a square number" << endl;
  21. 21} else {
  22. 22cout << n << " is not a square number" << endl;
  23. 23}
  24. 24return 0;
  25. 25}
▶ 输入样例
16
▶ 输出样例
16 is a square number
🪜 补全思路 + 模拟样例(n=16 是平方数;n=20 不是)
1 ① i 从 1 开始枚举(最小的正整数)
2 ② bound 取 √num(或 num),枚举上界
3 ③ 判断条件 i×i == num
4 ④ 找到则 return true;⑤ 枚举完没找到 return false
📊 n=16:i=1..4,当 i=4 时 4×4=16 ✓ → 输出「16 is a square number」
n=20:i=1..4,1,4,9,16 都 ≠ 20 ✗ → 输出「20 is not a square number」
第33题 ① 处应填( ) 简单

📝 填循环变量 i 的初值。

题意:找正整数 x,使 x² = n。最小的正整数是 1,所以 i 从 1 开始枚举。

A. 1 B. 2 C. 3 D. 4
✅ 答案:A. 1

从 1 开始枚举(n=1 时 1²=1 也是完全平方数,不能漏)。

第34题 ② 处应填( ) 中等

📝 填循环上限 bound。

如果存在 i² = num,那么 i 最多到 √num,向下取整就是 ⌊√num⌋。

用 C++ 写法:(int)floor(sqrt(num))。

A. (int)floor(sqrt(num)-1) B. (int)floor(sqrt(num)) C. floor(sqrt(num/2))-1 D. floor(sqrt(num/2))
✅ 答案:B. (int)floor(sqrt(num))

sqrt 返回 double,floor 向下取整,(int) 转型为整型。注意 (int) 写在 floor 外面才能保证正确。

💡 写法差异:通常还可以写成 (int)sqrt(num)(C++ 默认截断小数),效果相同。
⚠️ 但 sqrt(num) 有精度问题,平方大的数(如 n=10¹⁸)时可能错,不能用于生产代码。
第35题 ③ 处应填( ) 简单

📝 填 if 的判断条件。

题意:判断 i² 是否等于 num。

A、C 是赋值(一个 =),不符合 if 条件;B 是 num == 2*i(两倍),错误;D 才是 num == i*i(平方)。

A. num = 2*i B. num == 2*i C. num = i*i D. num == i*i
✅ 答案:D. num == i*i

注意 = 是赋值(返回值),== 才是相等比较。

第36题 ④ 处应填( ) 中等

📝 填 return 的内容。已经找到 i² = num,函数应当返回 true。

C 是直接的 true;A 是 num = 2*i 也是个赋值表达式,赋值结果 2*i 是非零整数,隐式转 bool 仍为 true,效果等价。

官方最终答案:A 或 C 均可得分。

A. num = 2*i B. num == 2*i C. true D. false
✅ 答案:A 或 C(官方双选)

语义上选 C 最清晰;语法技巧上 A 利用赋值返回值也是 true。考试时按官方标准给分。

💡 C++ 冷知识:return num = 2*i; 是合法的,赋值表达式有返回值(赋值后的值),非零整数 → bool 真。 但这种写法实际工程中不要用,可读性差。
第37题 ⑤ 处应填( ) 简单

📝 填 for 循环跑完没找到时的返回值。

说明 1 ~ ⌊√num⌋ 中没有 i 满足 i² = num,num 不是完全平方数,返回 false。

A. num = i*i B. num != i*i C. true D. false
✅ 答案:D. false

循环结束都没匹配上,说明不是完全平方数,返回 false。

📚 完整还原代码:
bool isSquare(int num) {
    int i = 1;
    int bound = (int)floor(sqrt(num));
    for (; i <= bound; ++i) {
        if (num == i * i) {
            return true;
        }
    }
    return false;
}

🛠️ 完善程序 2 —— 汉诺塔问题

📌 题目

三根柱子 A、B、C。A 柱上有 n 个圆盘从小到大叠放,要把它们全部搬到 C 柱。每次只能移动一个盘子,且大盘必须在小盘下面。问移动方案。

这是经典的递归分治:要把 n 个盘子从 src 移到 tgt:

  1. 把上面 n-1 个盘子从 src 借助 tgt 挪到 tmp
  2. 把最下面那个大盘从 src 移到 tgt
  3. 把 n-1 个盘子从 tmp 借助 src 挪到 tgt
C++程序代码(点击行号可定位)
  1. 01#include <iostream>
  2. 02#include <vector>
  3. 03using namespace std;
  4. 04
  5. 05void move(char src, char tgt) {
  6. 06cout << "从柱子" << src << "挪到柱子" << tgt << endl;
  7. 07}
  8. 08
  9. 09void dfs(int i, char src, char tmp, char tgt) {
  10. 10if (i == ①) {
  11. 11move(②);
  12. 12return;
  13. 13}
  14. 14dfs(i - 1, ③);
  15. 15move(src, tgt);
  16. 16dfs(⑤, ④);
  17. 17}
  18. 18
  19. 19int main() {
  20. 20int n;
  21. 21cin >> n;
  22. 22dfs(n, 'A', 'B', 'C');
  23. 23return 0;
  24. 24}
▶ 输入样例
3
▶ 输出样例
从柱子A挪到柱子C
从柱子A挪到柱子B
从柱子C挪到柱子B
从柱子A挪到柱子C
从柱子B挪到柱子A
从柱子B挪到柱子C
从柱子A挪到柱子C
🪜 汉诺塔递归结构 + 模拟样例(n=3,共 7 步)
1 dfs(i, src, tmp, tgt):把 i 个盘子从 src 借道 tmp 搬到 tgt
2 递归出口:i==1 时只剩 1 个盘子,直接 move(src, tgt)
3 一般情况三步走:① dfs(i-1, src, tgt, tmp) 把上面 i-1 个搬到 tmp;② move(src, tgt) 把最大的盘子搬过去;③ dfs(i-1, tmp, src, tgt) 把 tmp 上的搬回来
4 总移动次数满足递推 T(n) = 2T(n-1) + 1,即 T(n) = 2ⁿ − 1
📊 n=3:T(3) = 2³ − 1 = 7 步
A→C,A→B,C→B,A→C,B→A,B→C,A→C
第38题 ① 处应填( ) 简单

📝 填递归终止条件。当 i == 1 时只剩一个盘子,直接移动即可。

A. 0 B. 1 C. 2 D. 3
✅ 答案:B. 1

剩 1 个盘子就是最简单的情形,直接 move 后返回。

第39题 ② 处应填( ) 简单

📝 填 move 的参数。i==1 时把唯一盘子从 src 直接搬到 tgt。

A. src, tmp B. src, tgt C. tmp, tgt D. tgt, tmp
✅ 答案:B. src, tgt

从源柱子 src 搬到目标柱子 tgt。

第40题 ③ 处应填( ) 中等

📝 填第一次递归的实参(把上 n-1 个盘子从 src 挪到 tmp,tgt 作辅助)。

对应形参 (src, tmp, tgt):起点=src,辅助=tgt,目标=tmp。

A. src, tmp, tgt B. src, tgt, tmp C. tgt, tmp, src D. tgt, src, tmp
✅ 答案:B. src, tgt, tmp

原参数顺序是 (src, tmp, tgt),要把 n-1 个盘子从 src → tmp(用 tgt 作中转),所以填 src, tgt, tmp。

💡 角色转换:在递归里,src/tgt/tmp 三根柱子的物理位置(A、B、C)不变,但「起点-辅助-目标」三个角色在变。
这次:起点 src → 辅助 tgt → 目标 tmp。
第41题 ④ 处应填( ) 中等

📝 填第二次递归的实参(把 n-1 个盘子从 tmp 挪到 tgt,src 作辅助)。

对应形参 (src, tmp, tgt):起点=tmp,辅助=src,目标=tgt。

A. src, tmp, tgt B. tmp, src, tgt C. src, tgt, tmp D. tgt, src, tmp
✅ 答案:B. tmp, src, tgt

起点=tmp(暂存的 n-1 个盘子所在柱),辅助=src(原起点变辅助),目标=tgt。

第42题 ⑤ 处应填( ) 简单

📝 填第二次递归的第一个参数(盘子数)。

第二次递归处理的是 n-1 个盘子,所以填 i-1。

A. 0 B. 1 C. i - 1 D. i
✅ 答案:C. i - 1

每次递归都减少 1 个盘子(最大盘已经搬走),剩下 n-1 个递归处理。

📚 完整还原代码:
void dfs(int i, char src, char tmp, char tgt) {
    if (i == 1) {
        move(src, tgt);
        return;
    }
    dfs(i - 1, src, tgt, tmp);   // 上 n-1 个:src → tmp
    move(src, tgt);                // 最大盘:src → tgt
    dfs(i - 1, tmp, src, tgt);   // 下 n-1 个:tmp → tgt
}

📚 汉诺塔移动次数

n 个盘子最少需要 2ⁿ - 1 步。比如 n=3 时需要 7 步。

验证:n=2 时 dfs(2,'A','B','C') 会输出:

 A→B、A→C、B→C(共 3 步 = 2²-1)

这是一个时间复杂度 O(2ⁿ)、空间复杂度 O(n) 的算法。

💡 题型分布与备考建议

📊 CSP-J 2024 考点分析

题型 题号 核心考点
阅读1 16-20 素数判断的试除上界、循环条件细节
阅读2 21-26 动态规划爬楼梯、DP 状态转移跟踪
阅读3 27-32 递归函数的手工展开、边界条件
完善1 33-37 完全平方数判定(试除 + sqrt 上界)
完善2 38-42 汉诺塔递归分治、柱子角色转换
🎯 备考建议:
  • 阅读程序:拿到题先在草稿上手动跟踪小样例,把代码「跑一遍」。
  • 动态规划:2024 年 DP 题变难了,要熟练爬楼梯、打家劫舍等经典模型。
  • 递归:一定要敢动手写出展开过程,别只盯着代码看。
  • 完善程序:先把整个算法想清楚,再填每个空。注意读代码的「上下文语义」。
  • 小心边界:n=0、n=1 这种小输入常常是判断题的反例素材。