2025年CSP-J初赛真题及答案解析
2025年CSP-J初赛真题及答案解析
一、单项选择题(共15题,每题2分,共计30分)
1、一个32位无符号整数可以表示的最大值,最接近下列哪个选项?()
A. (4 ×\times× 109^99) B. (3 ×\times× 1010^{10}10) C. (2 ×\times× 109^99) D. (2 ×\times× 1010^{10}10)
答案:A
解析:32位无符号整数的最大值是 (232^{32}32 - 1 = 4294967295),约等于 (4.294 ×\times× 109^99),因此最接近选项 A.
2、在C++中,执行 int x=255;cout<<(x&(x-1));后,输出的结果是?()
A. 255 B. 254 C. 128 D. 0
答案:B
解析:255的二进制表示为 11111111
,254的二进制表示为 11111110
。按位与操作 11111111 & 11111110
结果为 11111110
,即十进制254。
3、函数 calc(n) 的定义如下,则 calc(5) 的返回值是多少?()
int calc(int n){if(n<=1) return 1;if(n%2==0) return calc(n/2)+1;else return calc(n-1)+calc(n-2);}
A. 5 B. 6 C. 7 D. 8
答案:B
解析:
-
calc(5) 是奇数,返回 calc(4) + calc(3)。
-
calc(4) 是偶数,返回 calc(2) + 1。
- calc(2) 是偶数,返回 calc(1) + 1 = 1 + 1 = 2。
- 因此 calc(4) = 2 + 1 = 3。
-
calc(3) 是奇数,返回 calc(2) + calc(1) = 2 + 1 = 3。
-
所以 calc(5) = 3 + 3 = 6。
4、用5个权值10、12、15、20、25构造哈夫曼树,该树的带权路径长度是多少?()
A. 176 B. 186 C. 196 D. 206
答案:B
解析:
-
构造哈夫曼树步骤:
- 合并10和12,得22。
- 合并15和20,得35。
- 合并22和25,得47。
- 合并35和47,得82。
-
带权路径长度计算:
-
10(深度3):10×3=30
-
12(深度3):12×3=36
-
15(深度2):15×2=30
-
20(深度2):20×2=40
-
25(深度2):25×2=50
-
总和:30+36+30+40+50=186。
-
5、在一个有向图中,所有顶点的入度之和等于所有顶点的出度之和,这个总和等于?()
A. 顶点数 B. 边数 C. 顶点数+边数 D. 顶点数×2
答案:B
解析:在有向图中,每条边贡献一个出度(对起点)和一个入度(对终点),因此入度之和和出度之和都等于边数。
6、从5位男生和4位女生中选出4人组成一个学习小组,要求学习小组中男生和女生都有。有多少种不同的选择方法?()
A. 126 B. 121 C. 120 D. 100
答案:C
解析:
- 总选法:C(9,4) = 126。
- 全男选法:C(5,4) = 5。
- 全女选法:C(4,4) = 1。
- 因此有男有女的选法:126 - 5 - 1 = 120。
7、假设a、b、c都是布尔变量,逻辑表达式(a && b)||(!c && a)的值与下列哪个表达式不始终相等?()
A. a && (b || !c)
B. (a || !c) && (b || !c) && ( a|| a)
C. a && (!b || c)
D. !(!a || !b) || (a && !c)
答案:C
解析:逻辑表达式(a && b)||(!c && a)可以简化为 a && (b || !c)。现在比较各选项:
- 选项A: a && (b || !c) 与简化后的表达式相同,因此始终相等。
- 选项B: (a || !c) && (b || !c) && (a || a) 简化后为 (a || !c) && (b || !c) && a。当a为真时,等价于 b || !c;当a为假时,为假。因此始终相等。
- 选项C: a && (!b || c)。当a为真、b为假、c为真时,原表达式为 false,而选项C为 true,因此不始终相等。
- 选项D: !(!a || !b) || (a && !c) 简化后为 (a && b) || (a && !c) = a && (b || !c),因此始终相等。
8、已知f[n]=1,f[1]=1并且对于所有n ≥ 2有f[n]=(f[n-1]+f[n-2])%7,那么f[2025]的值是多少?()
A. 2 B. 4 C. 5 D. 6
答案:D
解析:
已知序列定义为 f[0]=1,f[1]=1,且对于 n≥2,有 f[n]=(f[n−1]+f[n−2])%7
该序列模 7 的周期为 16,即 f[n]=f[nmod 16]
计算 2025mod 16:2025mod 16=9
于是 f[2025]=f[9]]。
计算 f[9]:
-
f[0]=1
-
f[1]=1
-
f[2]=(1+1)%7=2
-
f[3]=(2+1)%7=3
-
f[4]=(3+2)%7=5
-
f[5]=(5+3)%7=8%7=1
-
f[6]=(1+5)%7=6%7=6
-
f[7]=(6+1)%7=7%7=0
-
f[8]=(0+6)%7=6%7=6
-
f[9]=(6+0)%7=6%7=6
因此 f[2025]=6
9、下列关于C++ string类的说法,正确的是?()
A. string对象的长度在创建后不能改变。
B. 可以使用+运算符直接连接一个string对象和一个char类型的字符。
C. string的length()和size()方法返回的值可能不同。
D. string对象必须以\0结尾,且这个结尾符计入length()。
答案:B
解析:
-
选项A:错误。std::string对象的长度在创建后可以改变,例如通过append()、push_back()、赋值等操作。
-
选项B:正确。std::string重载了+运算符,可以直接连接一个string对象和一个char类型的字符,例如:
std::string s = "hello"; char c = '!'; std::string result = s + c;
。 -
选项C:错误。std::string的length()和size()方法完全等价,都返回字符串中字符的数量,因此返回的值始终相同。
-
选项D:错误。std::string对象在内部可能以null字符(\0)结尾以便与C字符串兼容,但这个结尾符不计入length()或size()返回的值中。
10、考虑以下C++函数,在main函数调用solve后,x和y的值分别是?()
void solve(int &a, int b){a = a + b;b = a - b;a = a - b;
}
int main(){int x=5, y=10;solve(x, y);
}
A. 5, 10
B. 10, 5
C. 10, 10
D. 5, 5
答案:C
解析:
在给定的C++代码中,函数solve
的参数a
是引用传递(int &a
),而参数b
是值传递(int b
)。在main
函数中,调用solve(x, y)
时,x
通过引用传递,y
通过值传递。
函数执行过程:
a = a + b;
:a
是x
的引用,初始值为5,b
为10,因此x
变为5 + 10 = 15。b = a - b;
:a
(即x
)为15,b
为10,因此局部变量b
变为15 - 10 = 5,但y
不变。a = a - b;
:a
(即x
)为15,局部b
为5,因此x
变为15 - 5 = 10。
函数结束后,x
被修改为10,而y
保持不变,仍为10。
因此,x
和y
的值分别为10和10,对应选项C。
11、一个8×8的棋盘,左上角坐标为(1,1),右下角为(8,8)。一个机器人从(1,1)出发,每次只能向右或向下走一格。要到达(4,5),有多少种不同的路径?()
A. 20 B. 35 C. 56 D. 70
答案:B
解析:
机器人从(1,1)出发,到达(4,5),需要向下移动3步(从x=1到x=4)和向右移动4步(从y=1到y=5),总共移动7步。
路径数由组合数决定,即从7步中选择3步向下(或选择4步向右),因此路径数为C(7,3)或C(7,4)。计算C(7,3) = 35。
因此,从(1,1)到(4,5)的不同路径数为35。
12、某同学用冒泡排序对数组 ({6,1,5,2,4}) 进行升序排序,请问需要进行多少次元素交换?()
A. 5 B. 6 C. 7 D. 8
答案:B
解析:
模拟冒泡排序过程:
-
第一轮:比较并交换6和1、6和5、6和2、6和4,交换4次,数组变为 {1,5,2,4,6}{1,5,2,4,6}。
-
第二轮:比较并交换5和2、5和4,交换2次,数组变为 {1,2,4,5,6}{1,2,4,5,6}。
-
第三轮:无交换,排序完成。
总交换次数为4 + 2 = 6次。
13、十进制数72010_{10}10和八进制数2708_{8}8的和用十六进制表示是多少?()
A. 38816_{16}16 B. 3DE16_{16}16 C. 28816_{16}16 D. 99016_{16}16
答案:A
解析:
十进制数720₁₀和八进制数270₈的和计算如下:
首先,将八进制数270₈转换为十进制:
270₈ = 2 × 8² + 7 × 8¹ + 0 × 8⁰ = 2 × 64 + 7 × 8 + 0 × 1 = 128 + 56 + 0 = 184₁₀。
然后,计算和:
720₁₀ + 184₁₀ = 904₁₀。
最后,将904₁₀转换为十六进制:
904 ÷ 16 = 56 余 8
56 ÷ 16 = 3 余 8
3 ÷ 16 = 0 余 3
从下往上读余数,得到388₁₆。
14、一棵包含1000个结点的完全二叉树,其叶子结点的数量是多少?()
A. 499 B. 512 C. 500 D. 501
答案:C
解析:
对于一棵完全二叉树,叶子结点的数量可以通过公式计算。对于n个结点的完全二叉树,叶子结点的数量为ceil(n/2)或n - floor(n/2)。当n=1000时,叶子结点数量为1000 / 2 = 500。
15、给定一个初始为空的整数栈s和一个空的队列P,按顺序处理输入的整数队列A:7、5、8、3、1、4、2。处理规则:①若该数是奇数,压入栈s;②若该数是偶数且栈s非空,弹出栈顶元素加入队列P;③若该数是偶数且栈s为空,不操作。当队列A处理完毕后,队列P的内容是什么?()
A. 5, 1, 3
B. 7, 5, 3
C. 3, 1, 5
D. 5, 1, 3, 7
答案:A
解析:
根据处理规则,按顺序处理整数队列A:7、5、8、3、1、4、2。
• 处理7(奇数):压入栈s → s = [7]
• 处理5(奇数):压入栈s → s = [7, 5]
• 处理8(偶数):栈s非空,弹出栈顶元素5加入队列P → P = [5], s = [7]
• 处理3(奇数):压入栈s → s = [7, 3]
• 处理1(奇数):压入栈s → s = [7, 3, 1]
• 处理4(偶数):栈s非空,弹出栈顶元素1加入队列P → P = [5, 1], s = [7, 3]
• 处理2(偶数):栈s非空,弹出栈顶元素3加入队列P → P = [5, 1, 3], s = [7]
处理完毕后,队列P的内容为5、1、3,对应选项A。
二、阅读程序(程序输入不超过数组或字符串定义的范围;判断题正确填√,错误填×;除特殊说明外,判断题1.5分,选择题3分,共计40分)
(1)
1. #include <algorithm>
2. #include <cstdio>
3. #include <cstring>
4. inline int gcd(int a, int b){
5. if(b==0)
6. return a;
7. return gcd(b, a%b);
8. }
9. int main(){
10. int n;
11. scanf("%d", &n);
12. int ans=0;
13. for (int i=1; i<=n; ++i){
14. for(int j=i+1; j<=n; ++j){
15. for(int k=j+1; k<=n; ++k){
16. if(gcd(i,j)==1 && gcd(j,k)==1
17. && gcd(i,k)==1){
18. ++ans;
19. }
20. }
21. }
22. }
23. printf("%d\n", ans);
24. return 0;
25. }
判断题
-
(1分)当输入为2时,程序并不会执行第16行的判断语句。()
-
将第16行中的"&& gcd(i, k)=1"删去不会影响程序运行结果。()
-
当输入的n ≥ 3的时候,程序总是输出一个正整数。()
单选题
- 将第7行的gcd(b,a%b)改为gcd(a,a%b)后,程序可能出现的问题是()。
A. 输出的答案大于原答案。
B. 输出的答案小于原答案。
C. 程序有可能陷入死循环。
D. 可能发生整型溢出问题。
- 当输入为8的时候,输出为()。
A. 37 B. 42 C. 35 D. 25
- 调用gcd(36,42)会返回()。
A. 6 B. 252 C. 3 D. 2
答案及解析:
答案:
判断题:正确、错误、正确
单选题:B、D、A判断题
- 输入n=2时,循环变量i从1到2,j从i+1到2,但k从j+1开始,j+1=3,而n=2,因此内层循环不会执行,第16行的判断不会执行。答案为正确。
- 原程序要求三元组(i, j, k)两两互质。删去gcd(i,k)==1后,只要求gcd(i,j)==1和gcd(j,k)==1,可能导致一些i和k不互质的三元组被计数,影响结果。例如,三元组(2,3,4)中gcd(2,3)=1和gcd(3,4)=1,但gcd(2,4)=2≠1,原程序不计数,删去后会计数。答案为错误。
- 对于n≥3,至少存在三元组(1,2,3)满足两两互质,因此输出至少为1,总是正整数。答案为正确。
单选题
- 修改后,gcd函数可能返回错误值。例如,gcd(3,2)原返回1,修改后返回3,导致条件gcd(i,j)==1等可能不满足,从而更少三元组被计数,输出答案小于原答案。答案为B. 输出的答案小于原答案。
- 计算n=8时,满足条件的三元组数量为25个(包含1的三元组14个,不包含1的三元组11个)。答案为D. 25。
- 使用欧几里得算法,gcd(36,42)计算过程为:gcd(36,42) → gcd(42,36) → gcd(36,6) → gcd(6,0) → 返回6。答案为A. 6。
(2)
1 #include <algorithm>2 #include <cstdio>3 #include <cstring>4 #define 11 long long5 int n, k;6 int a[200007];7 int ans[200007];8 int main(){9 scanf("%d%d", &n, &k);10 for (int i=1; i<=n; ++i){11 scanf("%d", &a[i]);12 }13 std::sort(a+1, a+n+1);14 n = std::unique(a+1, a+n+1) - a - 1;15 for(int i=1, j=0; i<=n; ++i){16 for(; j<i && a[i]-a[j+1]>k; ++j)17 ;18 ans[i] = ans[j] + 1;19 }20 printf("%d\n", ans[n]);21 return 0;22 }
判断题
22.当输入为“3 1 3 2 1”时,输出结果为2。()
23.假设输入的n为正整数,输出的答案一定小于等于n,大于等于1。()
24.将第14行的 n=std::unique(a+1,a+n+1)-a-1;删去后,有可能出现与原本代码不同的输出结果。()
单选题
25.假设输入的a数组和k均为正整数,执行第18行代码时,一定满足的条件不包括()。
A. j≤i B. a[i]−a[j]>k C. j≤n D. a[j]<a[i]
26.当输入的 n=100,k=2,a={1,2,…,100}时,输出为()。
A. 34 B. 100 C. 50 D. 33
27.假设输入的a数组和k均为正整数,但a数组不一定有序,若误删去第13行的 std::sort(a+1,a+n+1);程序有可能出现的问题有()。
A. 输出的答案比原本答案更大
B. 输出的答案比原本答案更小
C. 出现死循环行为
D. 以上均可能发生
答案及解析:
答案:
- 正确
- 正确
- 正确
- B
- A
- D
判断题
分析:输入n=3, k=1, 数组a=[3,2,1]。程序排序后a=[1,2,3],去重后n=3。计算ans[3]=2,输出2。因此,该说法正确。
分析:输出ans[n]表示分组数,至少为1,最多为n(每个元素一组),因此输出在1到n之间。正确。
分析:去重操作去掉后,会影响计数,导致结果不同。正确。
单选题
分析:执行第18行时,j满足j≤i、j≤n、a[j]<a[i](数组已排序去重),但a[i]-a[j]>k不一定成立(例如,当a[i]-a[j]≤k时)。因此,选项B不始终成立。
分析:数组排序去重后为1到100的连续整数。算法计算ans[i] = ceil(i/3),对于i=100,ceil(100/3)=34。
分析:删除排序后,数组无序,std::unique可能无法去除所有重复元素,且算法依赖排序,输出答案可能大于或小于正确值,但不会死循环。因此,以上均可能发生。
(3)
1 #include <algorithm>2 #include <stdio>3 #include <string>4 #define 11 long long5 int f[5007][5007];6 int a[5007], b[5007];7 int n;8 int main(){9 scanf("%d", &n);10 for (int i=1; i<=n; ++i){11 scanf("%d", &a[i]);12 }13 for(int i=1; i<=n; ++i){14 scanf("%d", &b[i]);15 }16 for(int i=1; i<=n; ++i){17 for(int j=1; j<=n; ++j){18 f[i][j] = std::max(f[i][j],std::max(f[i-1][j],f[i][j-1]));19 if(a[i]==b[j]){20 f[i][j] = std::max(f[i][j], f[i-1][j-1]+1);21 }22 }23 }24 printf("%d\n", f[n][n]);25 return 0;26 }
判断题
28.当输入“4 1 2 3 4 1 3 2 2”时,输出为2。()
29.当程序运行完毕后,对于所有的 1≤i,j≤n,都一定有 f[i][j]
≤f[n][n]
。()
30.将第18行的 f[i][j]
=std::max(f[i][j]
,std::max(f[i−1][j]
,f[i][j−1]
));删去后,并不影响程序运行结果。()
单选题
31.输出的答案满足的性质有()。
A. 小于等于n
B. 大于等于0
C. 不一定大于等于1
D. 以上均是
32.如果在16行的循环前加上以下两行:
std::sort(a+1,a+n+1);
std::sort(b+1,b+n+1);
则答案会()。
A. 变大或不变
B. 变小或不变
C. 一定变大
D. 不变
33.如果输入的 s=(1,2,…,n),而且b数组中数字均为1-n中的正整数,则上述代码等价于下面哪个问题:()。
A. 求b数组去重后的长度
B. 求b数组的最长上升子序列
C. 求b数组的长度
D. 求b数组的最大值
答案及解析:
答案:
- 正确
- 正确
- 错误
- D
- A
- B
判断题
分析:代码计算数组a和b的最长公共子序列(LCS)。a = [1,2,3,4], b = [1,3,2,2],LCS长度为2(例如[1,2]或[1,3])。因此,输出为2,说法正确。
分析:f
[i][j]
表示a的前i个元素和b的前j个元素的LCS长度,而f[n][n]
是整个序列的LCS长度。由于LCS长度随i和j增加而非递减,因此f[i][j] ≤ f[n][n]
始终成立。分析:第18行用于更新f[i][j]为当前值与f
[i-1][j]
和f[i][j-1]
的最大值,这是LCS算法的关键步骤。如果删去,f[i][j]只会在a[i]==b[j]时更新,导致LCS长度计算错误。因此,说法错误。单选题
分析:输出f
[n][n]
为LCS长度,其值小于等于n(A)、大于等于0(B)、且不一定大于等于1(如果无公共子序列则为0)(C)。因此,所有选项均正确。分析:排序后,a和b变为有序序列,LCS长度将变为两数组多集交集的大小(即常见元素的最小频率之和),该值大于或等于原始LCS长度。因此,答案变大或不变。
分析:a是严格递增序列1到n,b是1到n中的正整数。代码计算a和b的LCS,由于a的递增性,LCS等价于b的最长递增子序列(LIS)。
三、完善程序(单选题,每小题3分,共计30分)
(1)字符串解码
“行程长度编码”(Run-Length Encoding)是一种无损压缩算法,常用于压缩重复字符较多的数据,以减少存储空间。假设原始字符串不包含数字字符,压缩规则如下:
- ①如果原始字符串中一个字符连续出现N次(N≥2),在压缩字符串中表示为“字符+数字N”。例如,编码“A12”代表12个连续的字符A。
- ②如果原始字符串中一个字符只出现1次,在压缩字符串中表示为该字符本身。例如,编码“B”代表1个字符B。
以下程序实现读取压缩字符串并输出其原始的、解压后的形式,试补全程序。
1 #include <cctype>2 #include <iostream>3 #include <string>4 using namespace std;5 6 int main(){7 string z;8 cin >> z;9 string s = "";10 11 for (int i=0; i<z.length(); ){12 char ch = z[i];13 14 if( ① && isdigit(z[i+1])){15 i++;16 int count= 0;17 while (i<z.length() && isdigit(z[i])){18 count = ②;19 i++;20 }21 for (int j=0; j< ③ ; ++j){22 s += ch;23 }24 }else{25 s += ④ ;26 ⑤;27 }28 }2930 cout << s << endl;31 return 0;32 }
33.①处应填()
A. i < z.length() B. i - 1 ≥ 0 C. i + 1 < z.length() D. isdigit(z[i])
34.②处应填()
A. count + (z[i]-‘0’) B. count * 10 + (z[i]-‘0’) C. z[i]-‘0’ D. count+1
35.③处应填()
A. count-1 B. count C. 10 D. z[i]-‘0’
36.④处应填()
A. z[i+1] B. ch C. z.back() D. (char)z[i]+1
37.⑤处应填()
A. i- B. i = i+2 C. i++ D. //不执行任何操作
答案及解析:
答案:
- C
- B
- B
- B
- C
分析:
- ①处应检查当前索引i的下一个字符是否存在且为数字,因此需要确保i+1在字符串z的范围内,故填
i + 1 < z.length()
,对应选项C。- ②处用于构建重复次数count,由于数字可能有多位,需要逐位计算,故填
count * 10 + (z[i]-'0')
,对应选项B。- ③处指定重复添加字符ch的次数,即count的值,故填
count
,对应选项B。- ④处在else分支中处理单个字符,直接添加当前字符ch,故填
ch
,对应选项B。- ⑤处在else分支中需要将索引i递增以处理下一个字符,故填
i++
,对应选项C。
(2)精明与糊涂
有N个人,分为两类:
- ①精明人:永远能正确判断其他人是精明还是糊涂;
- ②糊涂人:判断不可靠,会给出随机的判断。
已知精明人严格占据多数,即如果精明人有k个,则满足k > N/2。你只能通过函数query(i,j)让第i个人判断第j个人;返回true表示判断结果为“精明人”;返回false表示判断结果为“糊涂人”。目标是通过互相判断,找出至少一个百分之百确定的精明人(无需关心query(i,j)的内部实现)。
以下程序利用“精明人占多数”的优势,通过“消除”过程让人们互相判断并抵消,经过若干轮抵消后,最终留下的候选者必然属于多数派(精明人),试补全程序。
1 #include <iostream>2 #include <vector>3 using namespace std;4 5 int N;6 bool query(int i, int j);7 8 int main(){9 cin >> N;10 11 int candidate = 0;12 int count = ①;13 14 for(int i=1; i<N; ++i){15 if(②){16 candidate = i;17 count = 1;18 }else{19 if(③){20 ④;21 }else{22 count++;23 }24 }25 }2627 cout << ⑤ << endl;28 return 0;29 }
38.①处应填()
A. 0 B. 1 C. N D. -1
39.②处应填()
A. count < 0
B. count == 1
C. count == 0
D. query(candidate, i) == false
40.③处应填()
A. query(candidate,i)==false
B. query(i,candidate)==true
C. query(candidate,i)==false && query(i,candidate)==
false
D. query(candidate,i)==false || query(i,candidate)==
false
41.④处应填()
A. count-- B. break C. count++ D. 0
42.⑤处应填()
A. N-1 B. count C. candidate D. 0
答案及解析:
答案:
- B
- C
- D
- A
- C
分析:
- ①处初始化计数器count,由于初始候选者为索引0,因此count应初始为1,对应选项B。
- ②处条件用于判断是否需要设置新的候选者,当count为0时表示当前没有候选者,因此需要设置新的候选者,对应选项C。
- ③处条件用于检查候选者和当前人i的判断是否至少有一个为false(即至少有一个判断为糊涂),这表示至少有一个是糊涂人,因此需要抵消,对应选项D。
- ④处执行抵消操作,即减少count,因此应填count–,对应选项A。
- ⑤处输出最终找到的精明人候选者,即candidate,对应选项C。
文末福利:
csp信奥赛复赛12大高频考点专题集训,助力你冲刺csp一等奖
课程直通链接:
https://edu.csdn.net/course/detail/40437
csp信奥赛复赛集训课涵盖12大高频考点专题集训内容:
1、语法基础专题
2、数学思维专题
3、模拟算法专题
4、排序算法专题
5、贪心算法专题
6、二分算法专题
7、前缀和&差分
8、深度优先搜索
9、广度优先搜索
10、动态规划专题
11、栈和队列专题
12、树和图专题