试卷解析 · 理解每一步
普及组 CSP-J 2026 初赛模拟卷 7
CSPJ-2026-R1-Mock-7
试卷解析总览,可直接查看每题答案与解析。
第 1 题(单选题,2 分)
计算机内存中的每一个基本单元,都被赋予一个唯一的序号,称为( )。
正确答案D
解析详情
内存按基本单元组织,处理器通过地址定位要读写的单元;每个单元对应的唯一序号就是地址。
第 2 题(单选题,2 分)
计算 的二进制结果是( )。
正确答案A
解析详情
按十六进制逐位相加得 。
每个十六进制位换成 4 位二进制,得到 。
第 3 题(单选题,2 分)
十进制数 的八位二进制补码是( )。
正确答案B
解析详情
的八位原码为 ;表示 时逐位取反得 ,再加 得 。
第 4 题(单选题,2 分)
12 张扑克牌,分别为黑桃、红心、梅花、方块的 J、Q、K。从中抽取三张牌,如果有对子就能获胜;如果没有对子,有黑桃 K 也能获胜。获胜的概率是( )。
正确答案A
解析详情
总取法为 。失败必须三种点数各取一张且不含黑桃 K,共 种。
获胜概率为 。
第 5 题(单选题,2 分)
一个链表的节点定义为 `struct node { int data; node* next; };`。该链表中的 `data` 数值从小到大排列。现在想在指针 `node* a` 指向的节点后插入一个 `data` 为 20 的新节点,正确的操作指令是( )。
node* newnode;
a->data = 20;
a->next = newnode;
newnode->data = a->data;node* newnode;
newnode->data = 20;
a->next = newnode;
a->data = newnode->data;node* newnode;
newnode->data = 20;
a->next = newnode;
newnode->next = a->next;node* newnode;
newnode->data = 20;
newnode->next = a->next;
a->next = newnode;正确答案D
解析详情
假定 newnode 已指向分配好的新节点,应先令 newnode->next 保存 a 原来的后继,再令 a->next 指向 newnode。
这样链条由 a->原后继变为 a->newnode->原后继,不会丢失后半段链表。
第 6 题(单选题,2 分)
24 点游戏指的是用给出的四个数字结合加、减、乘、除运算凑出 24。用 1、4、5、6 算 24 点,下列后缀表达式中正确的是( )。
正确答案C
解析详情
选项 C 依次计算 、,最后计算 。
第 7 题(单选题,2 分)
在 C++ 语言中,`char K = (char)('J' - 7)`,那么变量 `K` 代表的字母是( )。
正确答案B
解析详情
英文字母编码连续,'J' 向前移动 7 个位置依次经过 I、H、G、F、E、D,到达 'C',所以 K 表示字符 'C'。
第 8 题(单选题,2 分)
字符 A~F 的出现频率分别为 0.04、0.09、0.11、0.12、0.15、0.49,则这些字符所对应的哈夫曼编码可能为( )。
正确答案C
解析详情
依次合并最小权值:,,,再合并 与 。
因此频率 的 F 可用 1 位码,A、B 最深;C 中码长与该树一致且所有码互为非前缀。
第 9 题(单选题,2 分)
下列选项中( )是操作系统的名字。
正确答案C
解析详情
Android 负责管理移动设备的硬件资源并为应用提供运行环境,属于操作系统;Safari 和 WeChat 都是运行在操作系统上的应用软件。
第 10 题(单选题,2 分)
无向图 ,其中 ,。从顶点 a 开始进行深度优先搜索,遍历结果不可能为( )。
正确答案C
解析详情
若从 a 先访问 b,则 b 唯一尚未访问的邻点是 d,DFS 必须继续到 d,不能马上回到 a 再访问 c。
序列 abcdefg 的前三个顶点 a、b、c 因而不可能连续出现。
第 11 题(单选题,2 分)
给定二叉树的前序遍历序列为 `[a,b,c,d,f,g,h,i,e]`,中序遍历序列为 `[b,a,f,d,h,g,i,c,e]`,该二叉树的后序遍历结果是( )。
正确答案A
解析详情
前序首项 a 为根,中序将其分成左子树 b 和右子树 f,d,h,g,i,c,e;递归划分后右子树根为 c,其左根为 d、右子为 e。
按“左子树—右子树—根”得到后序序列 b,f,h,i,g,d,e,c,a。
第 12 题(单选题,2 分)
对整数序列 `5,17,28,96,13,15` 排序。第一次后为 `5,17,28,13,15,96`,第二次后为 `5,17,13,15,28,96`,第三次后为 `5,13,15,17,28,96`。使用的排序算法可能是( )。
正确答案A
解析详情
每一趟都把当前未排序部分的最大值向右交换到末端:第一趟固定 96,第二趟固定 28,第三趟固定 17。
这正是从左到右进行冒泡排序的中间状态。
第 13 题(单选题,2 分)
以下算法描述正确的是( )。
正确答案B
解析详情
算法具有有穷性,必须在执行有限个步骤后结束。时间复杂度描述基本操作次数随输入规模的增长关系,并非一次运行的实际耗时。
第 14 题(单选题,2 分)
小明的团队中只有 10 个成员(算上小明),他讨厌其中的两个人,又喜欢其中的三个人。他可以带任意多个成员出差,也可以自己一个人去。他不想和讨厌的人出差,也不想让出差的人中一个自己喜欢的都没有。他一共有( )种出差人选方案。
正确答案C
解析详情
排除 2 个讨厌的人后,可选者为 3 个喜欢的人和 4 个其他成员;喜欢的人至少选 1 个,有 种。
其他 4 人任意选择,共 种方案。
第 15 题(单选题,2 分)
一个递增的 `int` 类型数组 `n` 有 200 个元素,如果用二分法查找数组中的整数 `num`,最多需要比较( )次就可以断定该 `num` 是否在数组 `n` 中。
正确答案D
解析详情
最坏情况下每次比较后搜索区间约减半;,因此最多需要 8 次比较。
二、阅读程序(1)
1 | #include <iostream>
2 | using namespace std;
3 |
4 | bool isPrime(int n) {
5 | for (int i = 2; i < n; ++i)
6 | if (n % i == 0) return false;
7 | return true;
8 | }
9 | int Euler(int n) {
10 | int ret = n;
11 | for (int i = 2; i <= n; ++i)
12 | if (n % i == 0 && isPrime(i))
13 | ret = ret / i * (i - 1);
14 | return ret;
15 | }
16 |
17 | int main() {
18 | int x;
19 | cin >> x; // 保证 x 是正整数且不大于 100000000
20 | cout << Euler(x) << endl;
21 | return 0;
22 | }第 16 题(判断题,2 分)
当输入为 12 时,程序的输出为 4。( )
正确答案正确
解析详情
,ret 初值为 12;处理质因数 2 后变为 ,处理质因数 3 后变为 。
因此输出 4。
第 17 题(判断题,2 分)
将 `isPrime()` 函数中的 `for` 循环改成 `for (int i = 2; i * i < n; ++i)`,程序结果不变。( )
正确答案错误
解析详情
条件 i*i<n 会漏检平方根本身,例如 n=4 时循环一次也不执行,从而把 4 误判为素数。
Euler(4) 还会错误地再次处理因数 4,结果由 2 变成 0。
第 18 题(判断题,2 分)
将 `ret = ret / i * (i - 1);` 改成 `ret = ret * (i - 1) / i;`,结果可能与原程序结果不一样。( )
正确答案正确
解析详情
数学上两式等价,但 int 先乘可能溢出。例如 n=65537 时,原式先除得到 65536;改式先算 ,已超过 32 位有符号整数范围。
因此程序结果可能不同。
第 19 题(单选题,3 分)
将 `Euler()` 函数中的 `for` 循环改成 `for (int i = 2; i * 2 < n; ++i)`,下列哪一个输入的结果会与原程序结果不同?( )
正确答案B
解析详情
输入 26 时质因数为 2 和 13;新条件 i*2<26 会在 i=13 处失败,漏掉质因数 13。
原程序得到 ,修改后只处理 2,得到 13。
第 20 题(单选题,3 分)
当输入为 2025 时,程序的输出为( )。
正确答案D
解析详情
,其不同质因数只有 3 和 5。
程序依次更新 ret,得到 。
二、阅读程序(2)
1 | #include <iostream>
2 | #include <vector>
3 | using namespace std;
4 | int Deal(int n, int s, vector<int>& arr) {
5 | int count = 0;
6 | vector<int> sum;
7 | sum.push_back(0);
8 | for (int i = 1; i <= n; ++i) {
9 | sum.push_back(sum[i - 1] + arr[i]);
10 | }
11 | for (int i = 1; i <= n; ++i) {
12 | int L = i, R = n;
13 | while (L <= R) {
14 | int mid = (L + R) >> 1;
15 | if (sum[mid] - sum[i - 1] <= s) {
16 | L = mid + 1;
17 | } else {
18 | R = mid - 1;
19 | }
20 | }
21 | count += (R - i + 1);
22 | }
23 | return count;
24 | }
25 | int main() { // 输入均为正整数
26 | int n, s;
27 | cin >> n >> s;
28 | vector<int> arr(n + 1);
29 | arr[0] = 0;
30 | for (int i = 1; i <= n; ++i) cin >> arr[i];
31 | cout << Deal(n, s, arr) << endl;
32 | return 0;
33 | }第 21 题(判断题,2 分)
输入如下时,输出为 8。( )
4 5
1 2 3 4正确答案错误
解析详情
和不超过 5 的连续子数组分别为 [1]、[1,2]、[2]、[2,3]、[3]、[4],共 6 个。
程序对每个起点二分最远合法终点,所以输出 6 而不是 8。
第 22 题(判断题,2 分)
`sum[mid] - sum[i - 1]` 的意思为 `arr[i - 1] + arr[i] + ... + arr[mid]`。( )
正确答案错误
解析详情
sum[k] 保存 arr[1] 到 arr[k] 的和,因此 sum[mid]-sum[i-1] 恰好是 arr[i]+arr[i+1]+...+arr[mid]。
表达式中不包含 arr[i-1]。
第 23 题(判断题,2 分)
若输入完对 `arr` 数组排序,结果与原程序结果一样。( )
正确答案错误
解析详情
排序会改变哪些元素彼此连续,结果可能变化。例如 arr=[1,4,1]、s=2 时原序列有 2 个合法子数组,排序成 [1,1,4] 后有 3 个。
第 24 题(单选题,2 分)
若将主函数里的 `arr[0] = 0` 改为 `arr[0] = 1`,结果与改之前相比( )。
正确答案C
解析详情
Deal 中构造前缀和时只读取 arr[1] 到 arr[n],sum[0] 则由 sum.push_back(0) 单独确定。
arr[0] 从未参与任何后续计算,所以改为 1 后结果一定不变。
第 25 题(单选题,3 分)
若将 `Deal()` 函数开始的 `sum.push_back(0);` 改为 `sum.push_back(1);`,结果与改之前相比( )。
正确答案C
解析详情
把 sum[0] 从 0 改为 1 后,递推会使所有 sum[k] 都整体增加 1。
区间和仍为 ,常数相消,因此所有二分判断和最终计数不变。
第 26 题(单选题,3 分)
若输入的 满足 ,则输出必为( )。
正确答案A
解析详情
数组元素均为正且 s 大于全数组之和,所以任意连续子数组的和都小于 s,全部会被计数。
长度为 n 的数组共有 个连续子数组。
二、阅读程序(3)
1 | #include <iostream>
2 | using namespace std;
3 | int Function(int a, int b) {
4 | if (b == 0) return 1;
5 | if (a - 1 >= b) return Function(a - 1, b) + Function(a - 1, b - 1);
6 | return Function(a - 1, b - 1);
7 | }
8 | int main() { // 输入均为正整数
9 | int a, b;
10 | cin >> a >> b;
11 | cout << Function(a, b) << endl;
12 | return 0;
13 | }第 27 题(判断题,2 分)
输入 `5 2` 时,程序的输出为 10。( )
正确答案正确
解析详情
递归满足 ,边界 F(a,0)=1,对应组合数 。
所以 F(5,2)=。
第 28 题(判断题,2 分)
输入的 小于 时,`Function` 函数可能陷入无限递归。( )
正确答案错误
解析详情
当 a<b 时只会执行 F(a-1,b-1),每次都使 b 减 1;经过原 b 次调用后 b=0,触发返回 1。
因此即使 a 变为非正数,递归仍会在有限步内结束。
第 29 题(判断题,2 分)
程序的运行时间与 相关但与 无关,且 越大运行时间越长。( )
正确答案错误
解析详情
递归树是否分叉由 a-1>=b 决定:固定 b=1 时,增大 a 会增加递归层数;固定 a 时,b=a 只形成单支,而 b 接近 a/2 会产生大量双分支。
所以运行时间同时与 a、b 有关,且 b 越大并不保证运行越慢。
第 30 题(单选题,2 分)
输入 `10 7` 时,程序的输出为( )。
正确答案B
解析详情
Function(a,b) 计算 ,因此 Function(10,7)=。
计算得 。
第 31 题(单选题,3 分)
若输入的 不变, 依次取 ,则输出结果( )。
正确答案D
解析详情
输出依次为 ;相邻比值为 。
在 b 接近 a/2 前比值不小于 1,越过中点后不大于 1,所以序列先变大再变小。
第 32 题(单选题,3 分)
若将 `if (b == 0)` 改成 `if (b == 1)`,并输入 `12 5`,则程序的输出为( )。
正确答案B
解析详情
把边界改为 F(a,1)=1 后,递推结果变为 。
因此 F(12,5)=。
三、完善程序(1)朴素字符串匹配
(朴素字符串匹配)给定两个非空字符串 和 ,求 中有多少个与 相同的连续子串。保证 。
1 | #include <iostream>
2 | #include <string>
3 | using namespace std;
4 | int main() {
5 | int count = ①;
6 | string a, b;
7 | cin >> a >> b;
8 | for (int i = 0; i + b.length() <= a.length(); ++i) {
9 | int flag = ②;
10 | for (int j = 0; j < ③; ++j) {
11 | if (④) {
12 | flag = 1;
13 | break;
14 | }
15 | }
16 | if (flag == 0) ⑤;
17 | }
18 | cout << count << endl;
19 | return 0;
20 | }第 33 题(单选题,3 分)
① 处应填( )。
0123正确答案A
解析详情
count 表示已经找到的匹配子串数量;扫描开始前尚未发现任何匹配,因此必须初始化为 0。
之后每找到一次完整匹配再执行 ++count。
第 34 题(单选题,3 分)
② 处应填( )。
0123正确答案A
解析详情
flag=0 表示当前起点 i 尚未发现不相等字符;一旦 a[i+j]!=b[j],才把 flag 置为 1 并退出内层循环。
因此每次尝试新起点时都要从 0 开始。
第 35 题(单选题,3 分)
③ 处应填( )。
a.length()b.length()a.length() - 1b.length() - 1正确答案B
解析详情
要判断从 a[i] 开始的子串是否等于 b,必须逐一比较 b 的全部字符,所以 j 的范围是 0 到 b.length()-1。
循环上界应写 b.length()。
第 36 题(单选题,3 分)
④ 处应填( )。
a[j] != b[j]a[i + j] != b[i + j]a[j] != b[i + j]a[i + j] != b[j]正确答案D
解析详情
从主串起点 `i` 开始匹配时,模式串下标 `j` 对应主串下标 `i+j`,因此失配条件应为 `a[i+j] != b[j]`。外层循环已保证这一位置不会越界。
第 37 题(单选题,3 分)
⑤ 处应填( )。
breakcontinuecount = 1++count正确答案D
解析详情
内层循环结束后 flag==0 表示 b 的所有字符均与 a 从 i 开始的对应字符相同,找到一个匹配子串。
此时应执行 ++count,把匹配数增加 1。
三、完善程序(2)以最小代价删除字符
给定两个长度相同、仅含小写英文字母的字符串 和 。删除字母 `a`~`z` 的代价依次为 0~25,两个字符串都可以删除字符,求使它们相同的最小代价。
1 | #include <iostream>
2 | #include <vector>
3 | #include <algorithm>
4 | #include <cstring>
5 | using namespace std;
6 | int main() {
7 | string s1, s2;
8 | cin >> s1 >> s2;
9 | int n = s1.length();
10 | vector<vector<int>> dp(n + 1, vector<int>(n + 1));
11 | for (int i = 1; i <= n; ++i) {
12 | for (int j = 1; j <= n; ++j) {
13 | dp[i][j] = ①;
14 | if (②) {
15 | dp[i][j] = max(dp[i][j], ③);
16 | }
17 | }
18 | }
19 | int count = 0;
20 | for (int i = 0; i < n; ++i) count += ④;
21 | cout << ⑤ << endl;
22 | return 0;
23 | }第 38 题(单选题,3 分)
① 处应填( )。
max(dp[i][j], dp[i - 1][j - 1])max(dp[i - 1][j], dp[i][j - 1])min(dp[i][j], dp[i - 1][j - 1])min(dp[i - 1][j], dp[i][j - 1])正确答案B
解析详情
dp[i][j] 表示两个前缀可共同保留字符所节省的最大删除代价;若暂不匹配末尾字符,就从跳过 s1[i-1] 或跳过 s2[j-1] 中取较大值。
因此基础转移是 max(dp[i-1][j],dp[i][j-1])。
第 39 题(单选题,3 分)
② 处应填( )。
s1[i] == s2[j]s1[i - 1] == s2[j - 1]s1[i] == s2[j - 1]s1[i - 1] == s2[j]正确答案B
解析详情
dp 的 i、j 表示前缀长度,而 C++ 字符串从下标 0 开始,所以这两个前缀的末字符分别是 s1[i-1] 和 s2[j-1]。
只有二者相等时才能把该字符共同保留。
第 40 题(单选题,3 分)
③ 处应填( )。
dp[i - 1][j - 1] + s1[i - 1] - 'a'dp[i - 1][j] + s1[i - 1] - 'a'dp[i][j - 1] + s2[i] - 'a'dp[i - 1][j - 1] + 2 * (s1[i - 1] - 'a')正确答案D
解析详情
相等字符共同保留后,两个字符串都无需删除该字符;字符 s1[i-1] 的单次删除代价为 s1[i-1]-'a',总共节省其 2 倍。
故转移为 dp[i-1][j-1]+2*(s1[i-1]-'a')。
第 41 题(单选题,3 分)
④ 处应填( )。
s1[i] - 'a's2[i] - 'a's1[i] + s2[i] - 'a's1[i] + s2[i] - 2 * 'a'正确答案D
解析详情
count 先累计若删除两个字符串全部字符所需的总代价;位置 i 的贡献为 (s1[i]-'a')+(s2[i]-'a')。
合并后即 s1[i]+s2[i]-2*'a'。
第 42 题(单选题,3 分)
⑤ 处应填( )。
dp[n][n]count + dp[n][n]count - dp[n][n]count - 2 * dp[n][n]正确答案C
解析详情
count 是删除全部字符的总代价,dp[n][n] 是选择最优公共子序列后可节省的总代价。
因此最小实际删除代价为 count-dp[n][n]。