一、单项选择题
(共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项)
CSPJ-2024-R1
试卷解析总览,可直接查看每题答案与解析。
(共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项)
32 位 int 类型的存储范围是?( )
正确答案C
【答案】C
【考点】有符号整数的二进制表示
【解析】 32 位有符号 int 按补码表示时,最高位的权值为 ,其余 31 位最大和为 ,所以范围是 -2147483648 到 2147483647。
【易错点】负数端比正数端多表示一个数,不要把上下界都写成 。
计算 的结果,并选择答案的十进制值:()
正确答案A
【答案】A
【考点】不同进制数的转换与运算
【解析】 ,,,。 因此 。
【易错点】应先把各数按其下标所示进制转换为十进制,再进行四则运算。
某公司有 10 名员工,分为 3 个部门:A 部门有 4 名员工、B 部门有 3 名员工、C 部门有 3 名员工。现需要从这 10 名员工中选出 4 名组成一个工作小组,且每个部门至少要有 1 人。问有多少种选择方式?()
正确答案B
【答案】B
【考点】分类计数与组合数
【解析】 4 人覆盖 3 个部门,人数分配只能是 。 多出的 1 人来自 A、B、C 时,方案数分别为 、、,合计 126。
【易错点】不能只选择每部门 1 人,还要分类确定第 4 人来自哪个部门。
以下哪个序列对应数字 0 至 8 的 4 位二进制格雷码(Gray code)?()
正确答案D
【答案】D
【考点】格雷码
【解析】 格雷码要求相邻两个码字恰好只有 1 位不同。D 中从 0000 到 0100 的每次相邻变化都只翻转 1 位;题干列出的 8 个码字实际对应 0 至 7,按所给序列应选 D。
【易错点】只看码字是否互不相同不够,还必须逐对检查相邻码字的汉明距离为 1。
记 1KB 为 1024 字节(byte)、1MB 为 1024KB,那么 1MB 是多少二进制位(bit)?()
正确答案D
【答案】D
【考点】存储容量单位换算
【解析】 字节,每字节 8 位,所以共有 位。
【易错点】题目问的是 bit,算出字节数后还要乘 8。
以下哪个不是 C++ 中的基本数据类型?( )
正确答案C
【答案】C
【考点】C++ 基本数据类型
【解析】 int、float 和 char 都是 C++ 的基本数据类型;struct 是定义结构体这种复合数据类型的关键字,本身不是基本数据类型。
【易错点】不要把“用于定义类型的关键字”和“基本数据类型”混为一谈。
以下哪个不是 C++ 中的循环语句?( )
正确答案D
【答案】D
【考点】C++ 循环语句
【解析】 C++ 提供 for、while 和 do-while 三种循环结构,没有 repeat-until 语句。
【易错点】repeat-until 常见于 Pascal 等语言,不能直接当作 C++ 语法。
在 C/C++ 中,(char)('a'+13)与下面的哪一个值相等?()
正确答案B
【答案】B
【考点】字符编码与算术运算
【解析】 按题目采用的常见 ASCII 编码,小写英文字母连续,'a' 加 13 就是向后移动 13 个字符位置,得到 'n';再转换为 char 后值不变。
【易错点】从 'a' 到 'n' 的编码差是 13,不要把 'a' 自身误计为第 1 次偏移。
假设有序表中有 1000 个元素,则用二分法查找元素 X 最多需要比较( )次。
正确答案B
【答案】B
【考点】二分查找的比较次数
【解析】 二分查找每比较一次将范围缩小约一半。因为 ,最坏情况下最多比较 10 次。
【易错点】最坏比较次数应向上取整,不能只取 。
下面的哪一个不是操作系统名字?()
正确答案A
【答案】A
【考点】操作系统与应用软件
【解析】 Linux、Windows 和 macOS 都是操作系统;Notepad 是运行在操作系统之上的文本编辑应用程序。
【易错点】系统自带的软件也属于应用软件,不会因此变成操作系统。
在无向图中,所有顶点的度数之和等于()。
正确答案B
【答案】B
【考点】无向图的握手定理
【解析】 无向图的每条边分别给两个端点贡献 1 度,因此所有顶点度数之和为边数的两倍,即 。
【易错点】一条无向边在度数总和中要计算两次,而不是一次。
已知二叉树的前序遍历为 ,中序遍历为 ,请问该二叉树的后序遍历结果是?()
正确答案A
【答案】A
【考点】由前序和中序确定二叉树遍历
【解析】 前序首元素 A 是根;中序中 A 左侧构成以 B 为根、D 和 E 为左右孩子的子树,右侧构成以 C 为根、F 和 G 为左右孩子的子树。 按“左子树、右子树、根”的后序顺序得到 。
【易错点】后序遍历最后访问根节点 A,左右子树内部也要分别按后序排列。
给定一个空栈,支持入栈和出栈操作。若入栈操作的元素依次是 1 2 3 4 5 6,其中 1 最先入栈、6 最后入栈,下面哪种出栈顺序是不可能的?()
正确答案D
【答案】D
【考点】栈的后进先出性质
【解析】 按 D 输出 1、3、5 后,栈中从底到顶为 2、4,此时若要输出 2,必须先弹出压在它上面的 4,因此该序列不可能。 其余序列都能通过适时入栈和出栈实现。
【易错点】检查出栈序列时,不能越过当前栈顶直接取出更早入栈的元素。
有 5 个男生和 3 个女生站成一排,规定 3 个女生必须相邻。问有多少种不同的排列方式?
( )
正确答案A
【答案】A
【考点】捆绑法排列
【解析】 把 3 个女生视为一个整体,与 5 个男生共 6 个对象,可排列 种;女生内部还有 种排列。 总数为 。
【易错点】把女生捆绑后仍要计算她们在整体内部的排列。
编译器的主要作用是什么?()
正确答案B
【答案】B
【考点】编译器的作用
【解析】 编译器分析并翻译高级语言源代码,生成计算机可执行的目标代码或机器代码;它本身不等同于直接执行、调试或运行时内存管理。
【易错点】不要混淆编译器与解释器、调试器及操作系统的职责。
#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;
}当输入为 “10” 时,程序的第一个输出为 “4”,第二个输出为 “17”。()
正确答案正确
【答案】正确
【考点】素数判断与程序跟踪
【解析】 10 以内的素数为 2、3、5、7,共 4 个,元素之和为 ,所以程序输出“4 17”。
【易错点】1 不是素数,统计范围从 2 开始。
若将 isPrime(i) 函数中的条件改为 i <= n / 2,输入 “20” 时,countPrimes(20) 的输出将变为 “6”。()
正确答案错误
【答案】错误
【考点】素数判定的试除范围
【解析】 把上界扩大为 虽然增加了试除次数,但合数仍能找到因子、素数仍不会找到因子。20 以内仍有 8 个素数,countPrimes(20) 不会变为 6。
【易错点】扩大试除范围会降低效率,但不一定改变判定结果。
sumPrimes 函数计算的是从 2 到 n 之间的所有素数之和。
正确答案正确
【答案】正确
【考点】循环累加与函数语义
【解析】 sumPrimes 遍历 到 ,仅当 isPrime(i) 为真时执行 `sum += i`,因此累加的正是该范围内所有素数。
【易错点】循环条件是 `i <= n`,所以当 n 本身为素数时也会计入。
当输入为 “50” 时,sumPrimes(50) 的输出为()。
正确答案B
【答案】B
【考点】素数枚举与累加
【解析】 50 以内素数为 2、3、5、7、11、13、17、19、23、29、31、37、41、43、47。 依次相加得到 328,因此 sumPrimes(50) 返回 328。
【易错点】49 是 ,不是素数;50 也不会被累加。
如果将 for (int i = 2; i * i <= n; i++) 改为 for (int i = 2; i <= n; i++)
输入 “10” 时,程序的输出()。
正确答案A
【答案】A
【考点】素数判定循环边界
【解析】 循环若执行到 `i == n`,任何 都会因 `n % n == 0` 被判为非素数。 因此 2 到 10 的数全部无法通过 isPrime,程序不能正确得到原来的素数个数与和。
【易错点】试除不能包含 n 自身,否则素数也会被自己的整除关系误判。
#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;
}当输入的 cost 数组为 时,程序的输出为 15。()
正确答案正确
【答案】正确
【考点】动态规划状态转移
【解析】 初值为 ,随后 、。 函数返回 。
【易错点】最终答案是最后两个状态的较小值,不一定是 dp[n]。
如果将 dp[i-1] 改为 dp[i-3],程序可能会产生编译错误。()
正确答案错误
【答案】错误
【考点】数组越界与编译、运行错误
【解析】 代码语法和类型仍然合法,通常可以通过编译;但循环第一次取 `dp[i-3]` 时 ,访问的是 `dp[-1]`,会造成越界访问和未定义行为。
【易错点】下标越界属于运行期问题,不能笼统地判断为编译错误。
程序总是输出 cost 数组中最小的元素。()
正确答案错误
【答案】错误
【考点】动态规划结果的含义
【解析】 程序根据相邻状态计算到达各位置的累计最小花费,而不是直接求数组最小值。例如 cost 为 时输出 15,但数组最小元素是 10。
【易错点】状态转移中的 min 比较的是两条累计路径,不是 cost 中的单个元素。
当输入的 cost 数组为 时,程序的输出为()。
正确答案A
【答案】A
【考点】动态规划递推计算
【解析】 由 依次得到 。 最终返回 。
【易错点】每个 dp 状态都包含当前位置花费,最后还要取 dp[n] 与 dp[n-1] 的较小值。
如果输入的 cost 数组为 ,程序的输出为()
正确答案B
【答案】B
【考点】动态规划递推计算
【解析】 初值 ,继续递推得到 。 函数返回 。
【易错点】遇到较小的 cost 不代表累计花费立即最小,必须结合前两个 dp 状态。
若将代码中的 min(dp[i-1], dp[i-2]) + cost[i-1] 修改为 dp[i-1] + cost[i-2], 输入 cost 数组为 {5, 10, 15} 时,程序的输出为()。
正确答案A
【答案】A
【考点】修改后的递推式跟踪
【解析】 修改后 。对 ,有 、、,最终返回 。
【易错点】新式使用的是 cost[i-2],且返回语句仍取最后两个 dp 值的较小者。
#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;
}当输入为 “2 3” 时,customFunction(2, 3) 的返回值为 “64”。()
正确答案错误
【答案】错误
【考点】递归函数返回值
【解析】 customFunction(2,3) 会把 2 累加 4 次,返回 ;64 是 main 中对返回值平方后的最终输出,不是函数返回值。
【易错点】要区分 customFunction 的返回值与 `pow(result, 2)` 产生的程序输出。
当 b 为负数时,customFunction(a, b) 会陷入无限递归。()
正确答案正确
【答案】正确
【考点】递归终止条件
【解析】 递归只在 `b == 0` 时终止;若 b 初始为负数,每次执行 `b-1` 会离 0 越来越远,递归无法正常结束并最终导致栈耗尽。
【易错点】参数发生变化不等于会逼近递归基,必须检查变化方向。
当 b 的值越大,程序的运行时间越长。()
正确答案正确
【答案】正确
【考点】递归时间复杂度
【解析】 对非负 b,每层递归把 b 减 1,直到 0,共进行与 b 成正比的调用,时间复杂度为 ;b 越大,运行时间通常越长。
【易错点】这里每层只产生一次递归调用,不是指数级递归。
当输入为 “5 4” 时,customFunction(5, 4) 的返回值为()。
正确答案B
【答案】B
【考点】递归展开与求值
【解析】 customFunction(a,b) 对非负 b 返回 。因此 customFunction(5,4) 返回 。
【易错点】基例 `b == 0` 仍返回一次 a,所以总共累加 b+1 个 a。
如果输入 x=3 和 y=3,则程序的最终输出为()。
正确答案C
【答案】C
【考点】递归返回值与幂运算
【解析】 customFunction(3,3) 返回 ,main 随后计算 `pow(12, 2)`,最终输出 144。
【易错点】程序输出的是递归结果的平方,而不是递归结果本身。
若将customFunction函数改为“return a + customFunction(a-1, b-1);”,并输入“3 3”,则程序的最终输出为()。
正确答案D
【答案】D
【考点】双参数递归与程序跟踪
【解析】 修改后 customFunction(3,3) 展开为 ,其中基例 customFunction(0,0) 返回 0。 main 输出 。
【易错点】a 和 b 会同时减 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;
}①处应填()
1234正确答案A
【答案】A
【考点】完全平方数的枚举起点
【解析】 题目要求寻找正整数 x,最小候选值是 1,因此循环变量 i 应从 1 开始,才能覆盖 。
【易错点】正整数范围不包含 0,不能遗漏最小候选值 1。
②处应填()
(int)floor(sqrt(num))-1(int)floor(sqrt(num))floor(sqrt(num/2))-1floor(sqrt(num/2))正确答案B
【答案】B
【考点】平方根与枚举上界
【解析】 若 num 是完全平方数,它的正整数平方根必为 ;枚举到 即可覆盖唯一可能的整数根,同时无需检查更大的 i。
【易错点】上界减 1 会漏掉恰好等于 的平方根。
③处应填()
num = 2 * inum == 2 * inum = i * inum == i * i正确答案D
【答案】D
【考点】完全平方数的判定条件
【解析】 完全平方数的定义是存在整数 i 使 ,因此条件应写为 `num == i * i`。
【易错点】判等必须使用 `==`,写成 `=` 会变成赋值表达式。
④处应填()
num = 2 * inum == 2 * itruefalse正确答案C(另接受:A)
【答案】C
【考点】布尔函数返回值
【解析】 进入该分支说明已经满足 `num == i * i`,即确认 num 是完全平方数,所以 isSquare 应立即返回 true。
【易错点】分支条件成立表示已经找到平方根,不能返回 false。
⑤处应填( )
num = i * inum != i * itruefalse正确答案D
【答案】D
【考点】枚举结束后的布尔返回值
【解析】 循环检查完 到 仍未命中 `num == i * i`,说明不存在整数平方根,应返回 false。
【易错点】循环结束代表查找失败,不能把默认返回值写成 true。
给定三根柱子,分别标记为 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');
}①处应填()
0123正确答案B
【答案】B
【考点】汉诺塔递归基
【解析】 当只剩 1 个圆盘时,可以直接从 src 移到 tgt,无需继续分解,因此递归终止条件应为 `i == 1`。
【易错点】本程序的基例内会执行一次 move,所以基例对应 1 个圆盘而不是 0 个。
②处应填()
src, tmpsrc, tgttmp, tgttgt, tmp正确答案B
【答案】B
【考点】汉诺塔基本移动
【解析】 在 `i == 1` 的基例中,唯一的圆盘应直接从源柱 src 移到目标柱 tgt,因此调用 `move(src, tgt)`。
【易错点】tmp 只是辅助柱,单个圆盘不需要先经过辅助柱。
③处应填()
src, tmp, tgtsrc, tgt, tmptgt, tmp, srctgt, src, tmp正确答案B
【答案】B
【考点】汉诺塔递归参数映射
【解析】 移动最大圆盘前,要先把上方 个圆盘从 src 移到 tmp,此时原 tgt 充当辅助柱。 按函数参数 `(src, tmp, tgt)` 的角色传入应为 `src, tgt, tmp`。
【易错点】递归调用填写的是三根柱子的角色顺序,不能只照抄原参数名次序。
④处应填()
src, tmp, tgttmp, src, tgtsrc, tgt, tmptgt, src, tmp正确答案B
【答案】B
【考点】汉诺塔递归参数映射
【解析】 最大圆盘移到 tgt 后,要把暂存在 tmp 的 个圆盘移到 tgt,此时 src 充当辅助柱。 因此三根柱子参数应依次为 `tmp, src, tgt`。
【易错点】第二次递归的源柱已变为 tmp,不能继续使用原 src 作为源柱。
⑤处应填()
01i - 1i正确答案C
【答案】C
【考点】汉诺塔递归规模缩减
【解析】 移动最大圆盘后,tmp 上还剩 个较小圆盘需要递归移到 tgt,所以调用规模应为 `i - 1`。
【易错点】递归规模必须严格减小,否则使用 i 会导致无法到达 `i == 1` 的基例。