2023年CSP-J入门级初赛完整真题及详细解析
CCF非专业级别软件能力认证第一轮(CSP-J1)入门级 C++语言试题
满分100分 | 试题纸10页 | 答题纸2页
📋 试卷结构概览

一、单项选择题
(共15题,每题2分,共计30分;每题有且仅有一个正确选项)
第1题 C++语言基础 ⭐
在C++中,下面哪个关键字用于声明一个变量,其值不能被修改?( )
【答案】B
【解析】
unsigned:无符号整型,只是去掉了符号位,并非不可修改const:修饰的变量具有不能被修改的特性,又称为"常变量/只读变量"static:修饰局部变量使其生命周期变为全局生命周期,但作用域不变;修饰全局变量/函数使其仅限当前文件使用mutable:用于常成员函数,允许修改非对象状态的值
第2题 数据表示与计算 ⭐⭐
八进制数 12345670₍₈₎ 和 07654321₍₈₎ 的和为( )
【答案】D
【解析】 八进制逢八进一。快速计算只需看后三位:670₍₈₎ + 321₍₈₎ = 211₍₈₎,结合高位逐位相加,最终结果为 22222211₍₈₎。
第3题 C++语言基础 ⭐⭐
阅读下述代码,请问修改 data 的 value 成员以存储 3.14,正确的方式是( )
unionData {int num;float value;char symbol;};unionData data;【答案】A
【解析】
union(共用体/联合体)中所有成员共用一块内存,所占内存为最大成员的内存。访问联合体成员与结构体一致:变量用.访问,指针用->访问。data是变量,故用data.value。
第4题 线性表 ⭐⭐
假设有一个链表的节点定义如下:
structNode {int data; Node* next;};现在有一个指向链表头部的指针 Node* head。如果想要在链表中插入一个新节点,其成员 data 的值为 42,并使新节点成为链表的第一个节点,下面哪个操作是正确的?( )
Node* newNode = new Node; newNode->data = 42; newNode->next = head; head = newNode; | |
Node* newNode = new Node; head->data = 42; newNode->next = head; head = newNode; | |
Node* newNode = new Node; newNode->data = 42; head->next = newNode; | |
Node* newNode = new Node; newNode->data = 42; newNode->next = head; |
【答案】A
【解析】 头插法三步走:① 创建新节点并设置 data 值;② 新节点的 next 指向当前 head;③ head 指向新节点。选项 D 缺少了第③步
head = newNode,新节点不会成为头节点。
第5题 树 ⭐⭐
根节点的高度为1,一棵拥有 2023 个节点的三叉树高度至少为( )
【答案】C
【解析】 "至少"意味着高度最小,即满三叉树。前 k 层节点总数为:
前7层累计1093 < 2023,前8层累计3280 ≥ 2023,故高度至少为8。
第6题 组合数学 ⭐⭐⭐
小明在某一天中依次有七个空闲时间段,他想要选出至少一个空闲时间段来练习唱歌,但他希望任意两个练习的时间段之间都有至少两个空闲的时间段让他休息,则小明一共有( )种选择时间段的方案。
【答案】B
【解析】 七个时间段编号为1~7,逐一枚举:
选择数量 方案 数量 选1个 1, 2, 3, 4, 5, 6, 7 7种 选2个 14, 15, 16, 17, 25, 26, 27, 36, 37, 47 10种 选3个 147 1种 合计 7 + 10 + 1 = 18种
第7题 算法基础 ⭐⭐
以下关于高精度运算的说法错误的是( )
【答案】C
【解析】 高精度乘法的时间复杂度为 O(m × n),其中 m 和 n 分别是两个整数的位数。也就是说,运算时间与两个整数的位数乘积有关,而非仅与较长者的位数有关。
第8题 栈与队列 ⭐⭐⭐
后缀表达式 6 2 3 + - 3 8 2 / + * 2 ^ 3 + 对应的中缀表达式是( )
【答案】A
【解析】 后缀转中缀的方法:将一个表达式看作一个数,用
ab+的形式进行逆替换。逐步还原过程:
2 3 +→(2+3)6 (2+3) -→(6-(2+3))8 2 /→(8/2)3 (8/2) +→(3+8/2)(6-(2+3)) (3+8/2) *→((6-(2+3))*(3+8/2))... 2 ^→((6-(2+3))*(3+8/2))^2... 3 +→((6-(2+3))*(3+8/2))^2+3
第9题 数据表示与计算 ⭐⭐
数 101010₍₂₎ 和 166₍₈₎ 的和为( )
【答案】D
【解析】 统一转十进制计算:
101010₍₂₎ = 32+8+2 = 42₍₁₀₎ 166₍₈₎ = 64+48+6 = 118₍₁₀₎ 42 + 118 = 160₍₁₀₎ 验证各选项:
A: 10110000₍₂₎ = 176 ≠ 160 ❌ B: 236₍₈₎ = 158 ≠ 160 ❌ C: 158 ≠ 160 ❌ D: A0₍₁₆₎ = 10×16 = 160 ✅
第10题 树 ⭐⭐⭐
假设有一组字符 {a, b, c, d, e, f},对应的频率分别为 5%, 9%, 12%, 13%, 16%, 45%。请问以下哪个选项是字符 a, b, c, d, e, f 分别对应的一组哈夫曼编码?( )
【答案】A
【解析】 哈夫曼树构建过程(每次选最小的两个合并):
[100] / \ [0] f(45%) / \ [55] e(16%) / \[26] [29] /\ / \a b c d5% 9% 12% 13%
f 频率最高(45%),编码最短,为 0e 编码为 110a 编码为 1111,b 编码为1110c 编码为 101,d 编码为100
第11题 树 ⭐⭐⭐
给定一棵二叉树,其前序遍历结果为:ABDECFG,中序遍历结果为:DEBACFG。请问这棵树的正确后序遍历结果是什么?( )
【答案】A
【解析】 由前序+中序还原二叉树:
前序第一个 A 是根节点 中序中 A 左边 DEB 是左子树,右边 CFG 是右子树 前序中 BDE 对应左子树,CFG 对应右子树 左子树:前序 BDE,中序 DEB → B 是根,D、E 是左子树(D 是 E 的左孩子) 右子树:前序 CFG,中序 CFG → C 是根,F、G 是右子树(F 是 G 的父节点) A / \ B C / \ D F \ \ E G后序遍历:D → E → B → G → F → C → A = EDBGFCA
第12题 图 ⭐⭐
考虑一个有向无环图,该图包括4条有向边:(1,2),(1,3),(2,4),和(3,4)。以下哪个选项是这个有向无环图的一个有效的拓扑排序?( )
【答案】B
【解析】 拓扑排序规则:每次选择入度为0的节点输出,并删除其出边。
步骤 入度为0的节点 选择 删除的边 1 节点1 1 (1,2), (1,3) 2 节点2, 3 2 (2,4) 3 节点3 3 (3,4) 4 节点4 4 — 拓扑序:1 → 2 → 3 → 4
第13题 数据表示与计算 ⭐
在计算机中,以下哪个选项描述的数据存储容量最小?( )
【答案】B
【解析】 数据存储容量从小到大排列:
bit(比特) < byte(字节,1B=8bit) < word(字,通常2/4/8字节) < kilobyte(千字节,1KB=1024B)
第14题 组合数学 ⭐⭐⭐
一个班级有10个男生和12个女生。如果要选出一个3人的小组,并且小组中必须至少包含1个女生,那么有多少种可能的组合?( )
【答案】A
【解析】 分类计算:
情况 计算 结果 1女2男 C(12,1) × C(10,2) = 12 × 45 540 2女1男 C(12,2) × C(10,1) = 66 × 10 660 3女0男 C(12,3) 220 合计:540 + 660 + 220 = 1420
第15题 软件系统 ⭐
以下哪个不是操作系统?( )
【答案】D
【解析】 HTML(HyperText Markup Language)是超文本标记语言,用于浏览器网页界面显示,不是操作系统。Linux、Windows、Android 都是操作系统。
二、阅读程序题
(程序输入不超过数组或字符串定义的范围;判断题正确填√,错误填×;除特殊说明外,判断题1.5分,选择题3分,共计40分)
程序一:海伦公式求三角形面积
01#include<iostream>02#include<cmath>03usingnamespace std;0405doublef(double a, double b, double c){06double s = (a + b + c) / 2;07returnsqrt(s * (s-a) * (s-b) * (s-c));08 }0910intmain(){11 cout.flags(ios::fixed);12 cout.precision(4);1314int a, b, c;15 cin >> a >> b >> c;16 cout << f(a, b, c) << endl;17return0;18 }程序功能: 使用海伦公式
S = √(s(s-a)(s-b)(s-c)),其中s = (a+b+c)/2,计算三角形面积,结果保留4位小数。
假设输入的所有数都为不超过1000的正整数,完成下面的判断题和单选题:
第16题(判断题,2分)
当输入为 "2 2 2" 时,输出为 "1.7321"。( )
【答案】√
【解析】 等边三角形,s = 3,面积 = √(3×1×1×1) = √3 ≈ 1.7321
第17题(判断题,2分)
将第7行中的 (s-b)*(s-c) 改为 (s-c)*(s-b) 不会影响程序运行的结果。( )
【答案】√
【解析】 乘法满足交换律,
(s-b)*(s-c) = (s-c)*(s-b),不影响结果。注意:此类问题需要关注交换后是否影响数据范围、精度或计算原理。
第18题(判断题,2分)
程序总是输出四位小数。( )
【答案】√
【解析】
main函数开始处设置了cout.flags(ios::fixed)和cout.precision(4),如果没有取消该格式设置,整个程序运行期间都会保持输出4位小数。
第19题(单选题,3分)
当输入为 "3 4 5" 时,输出为( )
【答案】A
【解析】 3-4-5是直角三角形,面积 = 3×4÷2 = 6.0000。也可带入海伦公式:s=6, S=√(6×3×2×1)=√36=6。
第20题(单选题,3分)
当输入为 "5 12 13" 时,输出为( )
【答案】B
【解析】 5-12-13是直角三角形,面积 = 5×12÷2 = 30.0000。带入海伦公式:s=15, S=√(15×10×3×2)=√900=30。
程序二:最长公共子序列与字符串旋转判断
01#include<iostream>02#include<vector>03#include<algorithm>04usingnamespace std;0506intf(string x, string y){07int m = x.size();08 int n = y.size();09 vector<vector<int>> v(m+1, vector<int>(n+1, 0));10for (int i = 1; i <= m; i++) {11for (int j = 1; j <= n; j++) {12if (x[i-1] == y[j-1]) {13 v[i][j] = v[i-1][j-1] + 1;14 } else {15 v[i][j] = max(v[i-1][j], v[i][j-1]);16 }17 }18 }19return v[m][n];20 }2122boolg(string x, string y){23if (x.size() != y.size()) {24returnfalse;25 }26returnf(x + x, y) == y.size();27 }2829intmain(){30 string x, y;31 cin >> x >> y;32 cout << g(x, y) << endl;33return0;34 }程序功能:
f(x, y):使用动态规划求字符串 x 和 y 的最长公共子序列(LCS)长度g(x, y):判断 y 是否为 x 的旋转字符串。将 x 拼接为 x+x,若 y 是 x+x 的子序列且长度相等,则 y 是 x 的旋转
第21题(判断题,1.5分)
f函数的返回值小于等于 min(n, m)。( )
【答案】√
【解析】 最长公共子序列的长度不可能超过两个字符串中较短者的长度,即 LCS ≤ min(m, n)。
第22题(判断题,1.5分)
f函数的返回值等于两个输入字符串的最长公共子串的长度。( )
【答案】×
【解析】子串 ≠ 子序列。子串要求字符在原串中连续出现,子序列只要求相对顺序不变但不要求连续。f函数求的是最长公共子序列(LCS),不是最长公共子串。
第23题(判断题,1.5分)
当输入两个完全相同的字符串时,g函数的返回值总是 true。( )
【答案】√
【解析】 若 x = y,则 x+x 中必然包含 y(因为 y 就是 x,而 x+x 包含 x),所以
f(x+x, y) = y.size(),返回 true。
第24题(单选题,3分)
将第19行中的 v[m][n] 替换为 v[n][m],那么该程序( )
【答案】D
【解析】
v的大小是(m+1) × (n+1)。当 m ≠ n 时,v[n][m]可能会越界访问(当 n > m 时,第二维下标 m 可能超过 n)。在C/C++中,数组越界是未定义行为,可能导致非正常退出。
第25题(单选题,3分)
当输入为 "csp-j p-jcs" 时,输出为( )
【答案】B
【解析】
x = "csp-j",y = "p-jcs",长度均为5 x+x = "csp-jcsp-j" y = "p-jcs" 是 "csp-j" 的旋转(将 "csp-j" 的前两个字符 "cs" 移到末尾得到 "p-jcs") 因此 f(x+x, y) = 5 = y.size(),g 返回 truecout输出 bool 类型时输出1(而非 "T"),故选 B
第26题(单选题,3分)
当输入为 "csppsc spsccp" 时,输出为( )
【答案】D
【解析】
x = "csppsc",y = "spsccp",长度均为6 x+x = "csppsccsppsc" y = "spsccp" 是 x 的旋转("csppsc" 旋转后可以得到 "spsccp") 因此 g 返回 true,输出 1
程序三:因子平方和
01#include<iostream>02#include<cmath>03usingnamespace std;0405intsolve1(int n){06return n * n;07 }0809 intsolve2(int n){10int sum = 0;11for (int i = 1; i <= sqrt(n); i++) {12if (n % i == 0) {13if (n / i == i) {14 sum += i * i;15 } else {16 sum += i * i + (n/i) * (n/i);17 }18 }19 }20return sum;21 }2223intmain(){24int n;25 cin >> n;26 cout << solve2(solve1(n)) << " " << solve1(solve2(n)) << endl;27return0;28 }程序功能:
solve1(n)= n²(求平方)solve2(n)= n 的所有因子的平方和(遍历到 √n,成对累加因子平方)主程序输出: solve2(n²)和(solve2(n))²
假设输入的 n 是绝对值不超过1000的整数,完成下面的判断题和单选题:
第27题(判断题,2分)
如果输入的n为正整数,solve2函数的作用是计算n所有的因子的平方和。( )
【答案】√
【解析】
solve2遍历 1 到 √n,对于每个因子 i,将 i² 和 (n/i)² 加入 sum。当 i = n/i(即 n 是完全平方数)时,只加一次 i²。这正是求所有因子平方和的正确做法。
第28题(判断题,2分)
第13~14行的作用是避免n的平方根因子i(或n/i)进入第16行而被计算两次。( )
【答案】√
【解析】 当 n 是完全平方数时,i = √n,此时 n/i = i。如果不特殊处理,第16行会将 i² 加两次。第13~14行的判断确保此时只加一次。
第29题(判断题,2分)
如果输入的n为质数,solve2(n)的返回值为 n²+1。( )
【答案】√
【解析】 质数的因子只有 1 和 n 本身。因此 solve2(n) = 1² + n² = 1 + n²。
第30题(单选题,4分)
如果输入的n为质数p的平方,那么solve2(n)的返回值为( )
【答案】B
【解析】 n = p²(p为质数),n 的因子有:1, p, p²(=n)。
solve2(n) = 1² + p² + n² = 1 + n + n² = n² + n + 1
(因为 p² = n)
第31题(单选题,3分)
当输入为正整数时,第一项减去第二项的差值一定( )
【答案】D
【解析】
第一项 = solve2(n²) = n² 的所有因子平方和 第二项 = (solve2(n))² = (n 的所有因子平方和)² 由柯西不等式,(Σaᵢ²)² ≥ Σ(aᵢ²)²,即第二项 ≥ 第一项,差值 ≤ 0。
当 n = 1 时:第一项 = solve2(1) = 1,第二项 = (solve2(1))² = 1,差值 = 0。 当 n = 5 时:第一项 = solve2(25) = 651,第二项 = (solve2(5))² = 26² = 676,差值 = -25 < 0。
所以差值 ≤ 0 且不一定小于0。
第32题(单选题,3分)
当输入为 "5" 时,输出为( )
【答案】C
【解析】 n = 5:
solve1(5) = 25 solve2(25):25 的因子有 1, 5, 25,平方和 = 1 + 25 + 625 = 651 solve2(5):5 的因子有 1, 5,平方和 = 1 + 25 = 26 solve1(26) = 26² = 676 输出:
651 676
三、完善程序题
(单选题,每小题3分,共计30分)
程序一:寻找被移除的元素 二分查找 ⭐⭐⭐
问题: 原有长度为 n+1、公差为1的等差升序数列,将数列输入到程序的数组时移除了一个元素,导致长度为 n 的升序数组可能不再连续,除非被移除的是第一个或最后一个元素。需要在数组不连续时,找出被移除的元素。
01#include<iostream>02#include<vector>0304usingnamespace std;0506intfind_missing(vector<int>& nums){07int left = 0, right = nums.size() - 1;08 while (left < right) {09 int mid = left + (right - left) / 2;10if (nums[mid] == mid + ①) {11 ②;12 } else {13 ③;14 }15 }16return ④;17 }1819intmain(){20int n;21 cin >> n;22vector<int> nums(n);23for (int i = 0; i < n; i++) cin >> nums[i];24int missing_number = find_missing(nums);25if (missing_number == ⑤) {26 cout << "Sequence is consecutive" << endl;27 } else {28 cout << "Missing number is " << missing_number << endl;29 }30return0;31 }算法分析: 本题使用二分查找。核心思路:对于公差为1的等差数列,如果 nums[mid] == mid + nums[0],说明左侧区间连续,缺失元素在右侧;否则缺失元素在左侧(含 mid)。
第33题 ①处应填( )
【答案】B
【解析】 判断条件应该是
nums[mid] == mid + nums[0]。对于连续等差数列,第 mid 个元素(从0开始)的值应为nums[0] + mid。如果满足,说明 [left, mid] 区间连续,缺失元素在右侧。验证:nums = [1, 2, 3, 5, 6],mid = 2,nums[2] = 3,mid + nums[0] = 2 + 1 = 3 ✅
第34题 ②处应填( )
【答案】A
【解析】 满足 if 条件说明左侧 [left, mid] 连续,缺失元素一定在右侧 [mid+1, right],因此
left = mid + 1。
第35题 ③处应填( )
【答案】C
【解析】 不满足 if 条件说明 nums[mid] 处已经不连续,缺失元素在 [left, mid] 区间(含 mid),因此
right = mid。注意不能写right = mid - 1,因为 mid 处可能就是缺失位置的下一个。
第36题 ④处应填( )
【答案】A
【解析】 循环结束时
left == right,left 指向缺失元素应出现的位置。被移除的元素值 =left + nums[0](即该位置原本应该的值)。
第37题 ⑤处应填( )
【答案】D
【解析】 题目说明"除非被移除的是第一个或最后一个元素"。当移除的是最后一个元素时,数列仍然连续,应输出 "Sequence is consecutive"。此时
find_missing返回的是最后一个元素的下一个值,即nums[n-1] + 1... 但更准确地说,当数组完全连续时,left 会走到最后一个位置,返回nums[n-1],所以判断条件为missing_number == nums[n-1]。
程序二:编辑距离 动态规划 ⭐⭐⭐
问题: 给定两个字符串,每次操作可以选择删除(Delete)、插入(Insert)、替换(Replace)一个字符,求将第一个字符串转换为第二个字符串所需要的最少操作次数。
01#include<iostream>02#include<string>03#include<vector>04usingnamespace std;0506intmin(int x, int y, int z){07returnmin(min(x, y), z);08 }0910intedit_dist_dp(string str1, string str2){11int m = str1.length();12int n = str2.length();13 vector<vector<int>> dp(m+1, vector<int>(n+1));1415for (int i = 0; i <= m; i++) {16for (int j = 0; j <= n; j++) {17if (i == 0)18 dp[i][j] = ①;19elseif (j == 0)20 dp[i][j] = ②;21elseif (③)22 dp[i][j] = ④;23else24 dp[i][j] = 1 + min(dp[i][j-1], dp[i-1][j], ⑤);25 }26 }27return dp[m][n];28 }2930intmain(){31 string str1, str2;32 cin >> str1 >> str2;33 cout << "Minimum number of operations: "34 << edit_dist_dp(str1, str2) << endl;35return0;36 }算法分析: 经典的编辑距离动态规划。dp[i][j] 表示将 str1 的前 i 个字符转换为 str2 的前 j 个字符所需的最少操作次数。
第38题 ①处应填( )
【答案】A
【解析】
dp[0][j]表示 str1 为空字符串,要变成 str2 的前 j 个字符,需要插入 j 个字符,因此dp[0][j] = j。
第39题 ②处应填( )
【答案】B
【解析】
dp[i][0]表示 str1 的前 i 个字符变成空字符串,需要删除 i 个字符,因此dp[i][0] = i。
第40题 ③处应填( )
【答案】A
【解析】 第24行的 else 分支处理字符不相等的情况(插入/删除/替换),因此第21行应判断字符相等。由于字符串下标从0开始,第 i 个字符对应
str1[i-1],第 j 个字符对应str2[j-1]。
第41题 ④处应填( )
【答案】B
【解析】 当
str1[i-1] == str2[j-1]时,当前字符匹配,无需额外操作,dp[i][j] = dp[i-1][j-1]。
第42题 ⑤处应填( )
【答案】C
【解析】 第24行
1 + min(dp[i][j-1], dp[i-1][j], ⑤)分别对应三种操作:
dp[i][j-1]→ 插入操作dp[i-1][j]→ 删除操作⑤ = dp[i-1][j-1]→ 替换操作三者取最小值 +1(一次操作代价)。
验证: str1 = "abc", str2 = "abd"
| 0 | ||||
| a(1) | ||||
| b(2) | ||||
| c(3) | 1 |
结果:将 "abc" 变成 "abd" 只需1次替换(c→d),输出
Minimum number of operations: 1✅
📊 全卷考点分布
| 选择题 | |
| 阅读程序 | |
| 完善程序 |
🎯 备考建议
基础知识要扎实:C++关键字、数据类型、进制转换、存储单位等选择题考点需要系统掌握 数据结构是重点:链表操作、二叉树遍历(前序+中序→后序)、哈夫曼编码、拓扑排序几乎每年必考 阅读程序要练习:先理解程序整体功能,再逐行分析。注意边界条件、数据类型、输出格式 完善程序抓思路:先读懂题目描述的算法思想,再分析每个空在算法中的角色。二分查找和动态规划是高频考点 动手计算不可少:初赛考的就是知识面和计算能力,组合数学、进制转换等需要大量练习
本文为2023年CSP-J入门级初赛完整真题及详细解析,共42题,满分100分。预祝各位考生取得优异成绩!
获取 2023-CSP-J入门级初赛完整真题及详细解析.pdf
请关注状元编程公众号,回复 2023CSP-J