试卷解析 · 理解每一步
2020 CCF CSP-J 第一轮(入门级 C++)
CSPJ-2020-R1
试卷解析总览,可直接查看每题答案与解析。
第 1 题(单选题,2 分)
在内存储器中每个存储单元都被赋予一个唯一的序号,称为()。
正确答案A
解析详情
内存按存储单元组织,每个单元的唯一序号用于定位读写位置,这个序号称为地址。
第 2 题(单选题,2 分)
编译器的主要功能是()。
正确答案A
解析详情
编译器把高级语言编写的源程序翻译为计算机可执行或可进一步链接的目标代码,即机器指令代码。
第 3 题(单选题,2 分)
设 , , ,以下逻辑运算表达式值为真的是()。
正确答案D
解析详情
代入 x=true、y=true、z=false,D 中 x∧y=true,z∨x=true,两者再做或运算仍为 true。其余三个表达式都含有必须成立的“∧z”,因此为 false。
第 4 题(单选题,2 分)
现有一张分辨率为 像素的 32 位真彩色图像。请问要存储这张图像,需要多大的存储空间?( )。
正确答案C
解析详情
所需位数为 2048×1024×32 bit,除以 8 得 8,388,608 Byte,即 8 MB(按 1 MB=1024² Byte 计算)。
第 5 题(单选题,2 分)
冒泡排序算法的伪代码如下:
输入:数组 L,n ≥ 1。输出:按非递减顺序排序的 L。
算法 BubbleSort:
FLAG ← n //标记被交换的最后元素位置
while FLAG > 1 do
k ← FLAG -1
FLAG ← 1
for j = 1 to k do
if L(j) > L(j+1) then do
L(j) ↔ L(j+1)
FLAG ← j对 n 个数用以上冒泡排序算法进行排序,最少需要比较多少次?( )。
正确答案C
解析详情
最好情况下数组已经有序,第一趟需要比较相邻元素 n-1 次,且没有发生交换,FLAG 保持为 1,外层循环随即结束。
第 6 题(单选题,2 分)
设 A 是 n 个实数的数组,考虑下面的递归算法:
XYZ (A[1..n])
if n=1 then return A[1]
else temp ← XYZ (A[1..n-1])
if temp < A[n]
then return temp
else return A[n]请问算法 XYZ 的输出是什么?()。
正确答案B
解析详情
递归先求 A[1..n-1] 的最小值 temp,再返回 temp 与 A[n] 中较小者。由递归归纳可知,最终返回整个数组的最小值。
第 7 题(单选题,2 分)
链表不具有的特点是()。
正确答案A
解析详情
链表结点通过指针相连,访问第 k 个元素通常要从表头依次遍历,不能像数组那样按下标随机访问。链表插入、删除时一般无需整体移动元素。
第 8 题(单选题,2 分)
有 10 个顶点的无向图至少应该有( )条边才能确保是一个连通图。
正确答案A
解析详情
n 个顶点的连通图至少有 n-1 条边,此时图是一棵树。代入 n=10,最少需要 10-1=9 条边。
第 9 题(单选题,2 分)
二进制数 1011 转换成十进制数是()。
正确答案A
解析详情
(1011)₂=1×2³+0×2²+1×2¹+1×2⁰=8+2+1=11。
第 10 题(单选题,2 分)
五个小朋友并排站成一列,其中有两个小朋友是双胞胎,如果要求这两个双胞胎必须相邻,则有()种不同排列方法?
正确答案A
解析详情
把双胞胎看成一个整体,与另外 3 人共 4 个对象,可排列 4! 种;双胞胎内部还有 2 种次序,所以共有 4!×2=48 种。
第 11 题(单选题,2 分)
下图中所使用的数据结构是()。









正确答案A
解析详情
图示结构的数据从同一端进入和离开,遵循后进先出(LIFO),因此是栈;具体进出方向需结合题图确认。
第 12 题(单选题,2 分)
独根树的高度为 1。具有 61 个结点的完全二叉树的高度为()。
正确答案D
解析详情
高度为 h 的完全二叉树最多有 2^h-1 个结点。2^5-1=31<61≤63=2^6-1,所以 61 个结点的完全二叉树高度为 6。
第 13 题(单选题,2 分)
干支纪年法是中国传统的纪年方法,由 10 个天干和 12 个地支组合成 60 个天干地支。由公历年份可以根据以下公式和表格换算出对应的天干地支。
天干=(公历年份)除以10所得余数
地支=(公历年份)除以12所得余数
例如,今年是2020年,2020除以10余数为0,查表为“庚”;2020除以12,余数为4,查表为“子”,所以今年是庚子年。
请问 1949 年的天干地支是()
正确答案C
解析详情
1949÷10 余 9,查天干表得“己”;1949÷12 余 5,查地支表得“丑”。组合为己丑。
第 14 题(单选题,2 分)
10 个三好学生名额分配到 7 个班级,每个班级至少有一个名额,一共有()种不同的分配方案。
正确答案A
解析详情
设 7 个班所得名额为正整数 x₁,…,x₇,满足和为 10。用隔板法在 9 个间隙中选 6 个,方案数为 C(9,6)=84。
第 15 题(单选题,2 分)
有五副不同颜色的手套(共10只手套,每副手套左右手各1只),一次性从中取6只手套,请问恰好能配成两副手套的不同取法有()种。
正确答案A
解析详情
先选恰好配成的 2 副手套,有 C(5,2)=10 种;其余 2 只要来自剩下 3 种颜色中的不同两种,有 C(3,2)×2²=12 种。总数为 10×12=120。
二、阅读程序(1)
1 | #include <cstdlib>
2 | #include <iostream>
3 | using namespace std;
4 |
5 | char encoder[26] = {'C', 'S', 'P', 0};
6 | char decoder[26];
7 | string st;
8 |
9 | int main() {
10 | int k = 0;
11 | for (int i = 0; i < 26; ++i)
12 | if (encoder[i] != 0) ++k;
13 | for (char x = 'A'; x <= 'Z'; ++x) {
14 | bool flag = true;
15 | for (int i = 0; i < 26; ++i)
16 | if (encoder[i] == x) {
17 | flag = false;
18 | break;
19 | }
20 | if (flag) {
21 | encoder[k] = x;
22 | ++k;
23 | }
24 | }
25 | for (int i = 0; i < 26; ++i)
26 | decoder[encoder[i] - 'A'] = i + 'A';
27 | cin >> st;
28 | for (int i = 0; i < st.length(); ++i)
29 | st[i] = decoder[st[i] - 'A'];
30 | cout << st;
31 | return 0;
32 | }第 16 题(判断题,1.5 分)
输入的字符串应当只由大写字母组成,否则在访问数组时可能越界。
( )
正确答案正确
解析详情
程序用 st[i]-'A' 作为 decoder 的下标。只有大写字母才能保证下标落在 0~25;其他字符可能产生负数或大于 25,导致越界访问。
第 17 题(判断题,1.5 分)
若输入的字符串不是空串,则输入的字符串与输出的字符串一定不一样。()
正确答案错误
解析详情
构造完成后,T~Z 在 encoder 中的位置与字母序号相同,因此 decoder 对这些字母的映射不变。例如输入“T”,输出仍为“T”。
第 18 题(判断题,1.5 分)
将第 12 行的“i < 26”改为“i < 16”,程序运行结果不会改变。
正确答案正确
解析详情
构造 encoder 时真正需要排除的是预置的 C、S、P,它们都位于数组前 3 项。把查重范围缩为前 16 项仍能识别这三个字母;后来追加的字母按递增顺序处理,不会再次出现。
第 19 题(判断题,1.5 分)
将第 26 行的“i < 26”改为“i < 16”,程序运行结果不会改变。
正确答案错误
解析详情
decoder 需要根据 encoder 的 26 个位置完整赋值。若只处理前 16 项,O、Q、R、T~Z 等字符对应的 decoder 项不会被正确设置,含这些字符的输入会改变结果。
第 20 题(单选题,3 分)
若输出的字符串为“ABCABCABCA”,则下列说法正确的是()。
正确答案A
解析详情
decoder 的映射中,输入 C、S、P 分别输出 A、B、C。因此输出“ABCABCABCA”对应的输入按“CSP”循环,必然同时含有 S 和 P。
第 21 题(单选题,3 分)
若输出的字符串为“CSPCSPCSPCSP”,则下列说法正确的是()。
正确答案D
解析详情
由 encoder 可得,输出 C、S、P 分别来自输入 P、R、N。因此输出“CSP”重复时,输入包含 P 和 R,符合 D。
二、阅读程序(2)
1 | #include <iostream>
2 | using namespace std;
3 |
4 | long long n, ans;
5 | int k, len;
6 | long long d[1000000];
7 |
8 | int main() {
9 | cin >> n >> k;
10 | d[0] = 0;
11 | len = 1;
12 | ans = 0;
13 | for (long long i = 0; i < n; ++i) {
14 | ++d[0];
15 | for (int j = 0; j + 1 < len; ++j) {
16 | if (d[j] == k) {
17 | d[j] = 0;
18 | d[j + 1] += 1;
19 | ++ans;
20 | }
21 | }
22 | if (d[len - 1] == k) {
23 | d[len - 1] = 0;
24 | d[len] = 1;
25 | ++len;
26 | ++ans;
27 | }
28 | }
29 | cout << ans << endl;
30 | return 0;
31 | }假设输入的 n 是不超过 的正整数,k 都是不超过 10000 的正整数,完成下面的判断题和单选题:
第 22 题(判断题,1.5 分)
若 k=1,则输出 ans 时,len=n。()
正确答案错误
解析详情
当 k=1,第一次将 d[0] 加到 1 后就触发进位,使 len 变为 2;此后更高位会累加到大于 1,len 并不会随 n 同步增长,因此 len=n 并非恒成立。
第 23 题(判断题,1.5 分)
若 k>1,则输出 ans 时,len 一定小于 n。()
正确答案错误
解析详情
取 n=1、k=2,循环结束时计数器只有一位,即 len=1,与 n 相等而不是小于 n,所以“一定小于”不成立。
第 24 题(判断题,1.5 分)
若 k>1,则输出 ans 时,k^{len} 一定大于 n。()
正确答案正确
解析详情
当 k>1 时,数组 d 保存 n 的 k 进制各位,len 是所需位数。len 位所能表示的 n 满足 n<k^len,因此循环结束时 k^len 一定大于 n。
第 25 题(单选题,3 分)
若输入的 n 等于 ,输入的 k 为 1,则输出等于()。
正确答案D
解析详情
当 k=1,每次执行 ++d[0] 都会使 d[0]==k,并恰好让 ans 增加 1。循环共执行 n=10^15 次,所以输出 ans=10^15。
第 26 题(单选题,3 分)
若输入的 n 等于 205,891,132,094,649(即 ),输入的 k 为 3,则输出等于()。
正确答案B
解析详情
从 0 累加到 n 时,第 j 位产生的进位次数为 ⌊n/3^j⌋。当 n=3^30,总进位数为 3^29+3^28+…+1=(3^30-1)/2。
第 27 题(单选题,3 分)
若输入的 n 等于 100,010,002,000,090,输入的 k 为 10,则输出等于()。
正确答案D
解析详情
对 k=10,总进位次数为 Σ⌊n/10^j⌋。代入 n=100010002000090,依次累加各级整除结果,得到 11,112,222,444,453。
二、阅读程序(3)
1 | #include <algorithm>
2 | #include <iostream>
3 | using namespace std;
4 |
5 | int n;
6 | int d[50][2];
7 | int ans;
8 |
9 | void dfs(int n, int sum) {
10 | if (n == 1) {
11 | ans = max(sum, ans);
12 | return;
13 | }
14 | for (int i = 1; i < n; ++i) {
15 | int a = d[i - 1][0], b = d[i - 1][1];
16 | int x = d[i][0], y = d[i][1];
17 | d[i - 1][0] = a + x;
18 | d[i - 1][1] = b + y;
19 | for (int j = i; j < n - 1; ++j)
20 | d[j][0] = d[j + 1][0], d[j][1] = d[j + 1][1];
21 | int s = a + x + abs(b - y);
22 | dfs(n - 1, sum + s);
23 | for (int j = n - 1; j > i; --j)
24 | d[j][0] = d[j - 1][0], d[j][1] = d[j - 1][1];
25 | d[i - 1][0] = a, d[i - 1][1] = b;
26 | d[i][0] = x, d[i][1] = y;
27 | }
28 | }
29 |
30 | int main() {
31 | cin >> n;
32 | for (int i = 0; i < n; ++i)
33 | cin >> d[i][0];
34 | for (int i = 0; i < n; ++i)
35 | cin >> d[i][1];
36 | ans = 0;
37 | dfs(n, 0);
38 | cout << ans << endl;
39 | return 0;
40 | }假设输入的 n 是不超过 50 的正整数, 、 都是不超过 10000 的正整数,完成下面的判断题和单选题:
第 28 题(判断题,1.5 分)
若输入 n 为 ,此程序可能会死循环或发生运行错误。()
正确答案错误
解析详情
题面中的 θ 表示 0。调用 dfs(0,0) 时不满足 n==1,但 for 循环条件 1<n 立即为假,函数直接返回,随后输出初值 ans=0,不会继续递归。
第 29 题(判断题,1.5 分)
若输入 n 为 20,接下来的输入全为 0,则输出为 0。()
正确答案正确
解析详情
所有 d[i][0]、d[i][1] 都为 0 时,每次合并的增量 s=a+x+|b-y| 也为 0。任意合并顺序的累计 sum 都是 0,最终输出 0。
第 30 题(判断题,1.5 分)
输出的数一定不小于输入的 d[i][0] 和 d[i][1] 的任意一个。()
正确答案错误
解析详情
当 n=1 时,dfs(1,0) 直接用 sum=0 更新 ans,输出为 0;而题设允许输入的 d[0][0]、d[0][1] 为正数,所以输出可以小于输入元素。
第 31 题(单选题,3 分)
若输入的 n 为 20,接下来的输入是 20 个 9 和 20 个 0,则输出为()。
正确答案B
解析详情
每个元素为 (9,0),把大小分别为 p、q 的两组合并,贡献为 9(p+q)。最大值由逐次把一个新元素并入已有组取得,组大小依次为 2~20,因此结果为 9×(2+3+…+20)=1881。
第 32 题(单选题,3 分)
若输入的 n 为 30,接下来的输入是 30 个 0 和 30 个 5,则输出为()。
正确答案C
解析详情
元素均为 (0,5),大小为 p、q 的两组合并时贡献为 5|p-q|。采用链式合并时各次贡献为 5×(0+1+…+28),总和为 5×406=2030。
第 33 题(单选题,4 分)
若输入的n为15,接下来的输入是15到1,以及15到1,则输出为()。
正确答案C
解析详情
每个元素两维相等,合并和为 L、R 的两组时贡献为 L+R+|L-R|=2max(L,R)。按程序取到的最大累计值为 2×(15+29+42+…+119)=2240。
三、完善程序(1)质因数分解
(质因数分解)给出正整数 n,请输出将 n 质因数分解的结果,结果从小到大输出。
例如:输入 n=120,程序应该输出 2 2 2 3 5,表示 。输入保证 。提示:先从小到大枚举变量 i,然后用 i 不停试除 n 来寻找所有的质因子。
试补全程序。
#include <stdio>
using namespace std;
int n, i;
int main() {
scanf("%d", &n);
for(i = ①; ② <= n; i++) {
③ {
printf("%d ", i);
n = n / i;
}
}
if(④)
printf("%d ", ⑤);
return 0;
}第 34 题(单选题,3 分)
①处应填()
1n - 120正确答案C
解析详情
最小质因数是 2,试除应从 i=2 开始。若从 1 开始,n/1 不会减小,还会导致循环无法结束。
第 35 题(单选题,3 分)
②处应填()
n / in / (i * i)i * ii * i * i正确答案C
解析详情
只需枚举到平方根:若 n 仍有两个大于 1 的因子,其中至少一个不超过 √n。因此循环条件应为 i*i<=n,即②填 i*i。
第 36 题(单选题,3 分)
③处应填()
if (n % i == 0)if (i * i <= n)while (n % i == 0)while (i * i <= n)正确答案C
解析详情
同一个质因子可能出现多次,例如 120 含三个 2。只要 n%i==0,就应反复输出 i 并令 n/=i,所以必须使用 while。
第 37 题(单选题,3 分)
④处应填()
n > 1n <= 1i < n / ii + i <= n正确答案A
解析详情
循环结束后若 n>1,剩余的 n 必为尚未输出的质因数,需要再输出一次;若 n==1 则已经分解完毕。
第 38 题(单选题,3 分)
⑤处应填()
2n / ini正确答案C
解析详情
④判断 n>1 后,要输出的正是试除后剩余的质因数 n,因此⑤填 n。
三、完善程序(2)最小区间覆盖
(最小区间覆盖)给出 n 个区间,第 i 个区间的左右端点是 。现在要在这些区间中选出若干个,使得区间 被所选区间的并覆盖(即每一个都在某个所选的区间中)。保证答案存在,求所选区间个数的最小值。
输入第一行包含两个整数 n 和 m ()。
接下来 n 行,每行两个整数 , ( , )。
提示:使用贪心法解决这个问题。先用 的时间复杂度排序,然后贪心选择这些区间。
试补全程序。
#include <iostream>
using namespace std;
const int MAXN = 5000;
int n, m;
struct segment { int a, b; } A[MAXN];
void sort() {
for (int i = 0; i < n; i++)
for (int j = 1; j < n; j++)
if (①) {
segment t = A[j];
②
}
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++)
cin >> A[i].a >> A[i].b;
sort();
int p = 1;
for (int i = 1; i < n; i++)
if (③)
A[p++] = A[i];
n = p;
int ans = 0, r = 0;
int q = 0;
while (r < m) {
while (④)
q++;
⑤;
ans++;
}
cout << ans << endl;
return 0;
}第 39 题(单选题,3 分)
①处应填()
A[j].b > A[j - 1].bA[j].a < A[j - 1].aA[j].a > A[j - 1].aA[j].b < A[j - 1].b正确答案B
解析详情
后续贪心需要区间按左端点 a 非递减排列。冒泡排序发现 A[j].a<A[j-1].a 时应交换相邻区间,因此①填该条件。
第 40 题(单选题,3 分)
②处应填()
$A[j + 1] = A[j]$; $A[j] = t$;$A[j - 1] = A[j]$; $A[j] = t$;$A[j] = A[j + 1]$; $A[j + 1] = t$;$A[j] = A[j - 1]$; $A[j - 1] = t$;正确答案D
解析详情
已有 t=A[j],交换 A[j-1] 与 A[j] 时,应先令 A[j]=A[j-1],再令 A[j-1]=t,正好对应 D。
第 41 题(单选题,3 分)
③处应填()
A[i].b > A[p - 1].bA[i].b < A[i - 1].bA[i].b > A[i - 1].bA[i].b < A[p - 1].b正确答案A
解析详情
左端点已递增,A[p-1] 是最后保留的区间。只有 A[i].b>A[p-1].b 时,新区间才能把覆盖范围向右推进,才应写入 A[p++];否则它被前一区间包含。
第 42 题(单选题,3 分)
④处应填()
正确答案A
解析详情
当前已覆盖到 r,应继续考察所有左端点不超过 r 的候选区间。q 的下一个位置必须存在且满足 A[q+1].a<=r,所以条件为 q+1<n && A[q+1].a<=r。
第 43 题(单选题,3 分)
⑤处应填()
q++正确答案B
解析详情
内层循环结束后,A[0..q] 的左端点都不超过 r;经过前面的去冗余处理,A[q] 的右端点最远。选择它后应更新 r=max(r,A[q].b)。