一、单项选择题
共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项。
试卷解析 · 理解每一步
CSPJ-2026-R1
试卷解析总览,可直接查看每题答案与解析。
共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项。
下列 C++ 数据类型中,能够精确存储 这个整数的是( )。
floatlong longdoubleint正确答案B
本题关键词是“精确存储”,需要同时考虑数值范围和表示精度。
在常见竞赛环境中,int 为 32 位有符号整数,最大值约为 ,不能容纳 。long long 通常为 64 位有符号整数,最大值为 ,范围足够,而且整数类型在范围内能精确表示每一个整数。
float、double 虽然能表示很大的数量级,但有效二进制位数有限;常见 double 只有 53 位二进制有效精度,到 附近已经无法区分相邻整数。这里 不能用它精确表示。
因此应选 long long,即 B。
十六进制数 2F5 转换为八进制数是( )。
1364163514051365正确答案D
可以先转二进制,再按三位一组转八进制:十六进制的一位对应二进制四位,所以 2、F、5 分别对应 0010、1111、0101。
拼接得到 001011110101,从右往左每三位分组为 001 | 011 | 110 | 101,分别对应八进制的 1、3、6、5。因此结果是 1365,选择 D。
也可用十进制验算:;而 ,两者表示同一个数。
执行下列 C++ 代码,输出是( )。
int a = 7, b = 3;
std::cout << a / b * b + a % b;正确答案C
a、b 都是 int,所以 a/b 执行整数除法,7/3 的商为 2;a%b 取余数,7%3=1。
乘法、除法、取余的优先级高于加法,其中连续的除法与乘法按从左到右结合,因此 a/b*b 等于 (7/3)*3=2*3=6。
最后加上余数 1,输出 7,选择 C。这也是整数除法的关系式:被除数=商×除数+余数。
不能先在实数范围内把 7/3 当作带小数的结果,也不能把 a/b*b 误读成 a/(b*b)。
初始时栈为空,将 1、2、3、4 依次入栈,入栈过程中允许随时出栈。下列出栈序列中不可能出现的是( )。
正确答案C
栈只能弹出栈顶元素,不能跳过上面的元素取出下面的元素。
若出栈序列以 3 开头,必须先依次压入 1、2、3,再弹出 3。此时栈从底到顶为 1、2,下一次若出栈,只能弹出 2,不能直接弹出 1。
所以 3,1,2,4 不可能出现,选择 C。即使此时把 4 压栈,也只会让 4 挡在更上方,仍无法先弹出 1。
对比序列 2,4,3,1:压入 1、2 后弹出 2,再压入 3、4,依次弹出 4、3、1,就可以实现。
一棵有 100 个结点的完全二叉树,其叶子结点个数是( )。
正确答案B
完全二叉树按层从上到下、每层从左到右编号为 1 到 100 时,编号 i 的左孩子编号为 2i,右孩子编号为 2i+1,超过 100 的孩子不存在。
只要 2i<=100,结点 i 就至少有一个孩子,因此编号 1 到 50 都不是叶子。其中结点 50 只有左孩子 100,也不能算叶子。
编号 51 到 100 的结点都没有孩子,共 个,选择 B。
最后一层只有编号 64 到 100 的 37 个结点,但上一层的 51 到 63 也没有孩子,所以叶子数不能只数最后一层。
执行下列代码后 s 的值是( )。
int s = 0;
for (int i = 1; i <= 100; i++)
if (i % 3 == 0 || i % 5 == 0)
s += i;正确答案D
条件中的 || 表示“或”:i 是 3 的倍数或 5 的倍数就累加一次;即使两个条件都成立,s+=i 也只执行一次。
1 到 100 中,3 的倍数为 3、6、……、99,共 33 个,和为 。
5 的倍数为 5、10、……、100,共 20 个,和为 。若直接把两者相加,15 的倍数被重复计算,需要减去 。
因此 s=1683+1050-315=2418,选择 D。循环包含 i=100,不能把最后一个 5 的倍数漏掉。
上楼梯每步可上 1 级、2 级或 3 级,从地面(可视为第 0 级)走到第 8 级台阶共有多少种不同走法( )。
正确答案D
设 f(i) 为到达第 i 级台阶的走法数。按最后一步分类:最后走 1 级时来自 i-1,走 2 级时来自 i-2,走 3 级时来自 i-3。这三类互不重复,合起来覆盖所有走法。
因此 。规定 f(0)=1,表示还没走任何一步这一种初始方案;负下标的项按 0 处理。
逐项计算得 f(1)=1,f(2)=2,f(3)=4,f(4)=7,f(5)=13,f(6)=24,f(7)=44,最后 f(8)=44+24+13=81,选择 D。
例如先走 1 级再走 2 级,与先走 2 级再走 1 级,是不同的走法,所以不能只统计各种步长用了几次。
下图为 网格,行号、列号均从 0 开始,# 为障碍,. 为可通行格:
S..#.
...#.
...#.
##..E
...#.从 S 出发做广度优先搜索(BFS):初始时把 S 入队;每次取出队首格子,按“上、下、左、右”(上 = 行号减 1,下 = 行号加 1,左 = 列号减 1,右 = 列号加 1)的顺序遍历它的四个相邻格子,越界、障碍或已访问的格子跳过,其余格子标记为已访问并入队。当 E 第一次入队时,已经入队过的格子(含 S 和 E)共有多少个( )。
正确答案C
用 (行,列) 表示格子,并严格按“上、下、左、右”检查相邻格。格子一入队就标记,之后不会重复入队。
从 S=(0,0) 开始,累计入队顺序为:①(0,0),②(1,0),③(0,1),④(2,0),⑤(1,1),⑥(0,2),⑦(2,1),⑧(1,2),⑨(2,2)。
随后处理 (2,2),加入⑩(3,2);处理 (3,2) 时,先向下加入⑪(4,2),再向右加入⑫(3,3)。
队列先处理 (4,2),向左加入⑬(4,1);之后处理 (3,3),上、下都是障碍,左侧已访问,向右才加入⑭E=(3,4)。
因此 E 第一次入队时,累计入队过 14 个格子,选择 C。此时 (4,1) 尚未出队,但它已经入队过,必须计数。
# 障碍 · 尚未入队 坐标从 0 开始;探索顺序:上 → 下 → 左 → 右
数字是累计入队序号,不是距离。E 在第 14 次入队;第 13 个格子此时仍在队列中。
满足 且 的正整数 共有多少个( )。
正确答案B
由 gcd(n,60)=6,可知 n 必须是 6 的倍数。令 n=6k,又因为 60=6×10,可将条件化为 ,即 gcd(k,10)=1。
范围 1<=n<=100 对应整数 1<=k<=16。与 10 互质,就是既不能被 2 整除,也不能被 5 整除。
1 到 16 中符合的 k 为 1、3、7、9、11、13,对应 n 为 6、18、42、54、66、78,共 6 个,选择 B。
例如 n=30 虽然是 6 的倍数,但 gcd(30,60)=30,不满足题目要求;只检查“是 6 的倍数”还不够。
某国硬币面值为 1 元、4 元、6 元且数量不限,凑出 9 元最少需要多少枚( )。
正确答案A
先给出一种可行方案:4+4+1=9,只需 3 枚,所以最少枚数不会超过 3。
再证明不能少于 3 枚:一枚硬币没有面值 9;两枚硬币所有可能的和为 1+1=2、1+4=5、1+6=7、4+4=8、4+6=10、6+6=12,都不等于 9。
因此至少要 3 枚,而已有 3 枚的方案,最优值就是 3,选择 A。
若每次贪心选最大面值,会先选 6,再用三个 1 补足,得到 4 枚,说明这种面值体系不能直接使用该贪心策略。
执行下列代码,输出是( )。
int a[5] = {1, 3, 5, 7, 9};
int *p = a + 2;
*(p - 1) = p[0] + p[2];
p[1] = *(a + 1) - a[0];
cout << a[1] << "," << a[3];14,138,1314,714,2正确答案A
p=a+2 表示 p 指向 a[2]。记住 p[k] 等价于 *(p+k),因此 p[0]=a[2]=5,p[2]=a[4]=9,*(p-1) 则是 a[1]。
第一条赋值将 a[1] 改为 5+9=14,数组变成 {1,14,5,7,9}。
接着 p[1] 对应 a[3],而 *(a+1) 读取的是刚更新后的 a[1]=14。因此 a[3]=14-a[0]=14-1=13。
最终输出 a[1] 和 a[3],中间有逗号,即 14,13,选择 A。指针始终指向原数组,赋值不会产生一个与数组分离的副本。
在含 1000 个互不相同元素的升序数组中,用二分法查找给定值(返回元素位置或报告不存在),最坏情况下需要与数组元素比较多少次( )。
正确答案D
按通常二分查找的计数方式,每次取一个中间元素与目标值比较,随后只保留一侧候选区间。
最坏情况下,候选元素个数可依次缩为 1000 → 500 → 250 → 125 → 62 → 31 → 15 → 7 → 3 → 1 → 0。每个箭头对应一次中间元素比较,共需要 10 次。
也可用 ,得到最坏比较次数 ,选择 D。
这里统计的是与多少个中间元素进行比较,不是把代码里 ==、< 等每个运算符分别计数。
数组 的前缀和数组 (即 )满足 。则 的值是( )。
正确答案C
前缀和 S[10] 包含 a[1] 到 a[10],而 S[9] 只包含 a[1] 到 a[9]。两者相减,前九项全部抵消,只留下 a[10]。
代入公式得 ,。
所以 ,选择 C。
进一步整理可得 ,令 i=10 也得到 58。
数轴上有 7 个点,坐标分别为 1、3、4、7、10、15、20。在数轴上选取一个整数坐标点 P,使 P 到这 7 个点的距离之和最小,这个最小距离和是( )。
正确答案A
将坐标从小到大排列后,中间的第 4 个数是 7。绝对距离和在中位数处最小,可以用配对说明原因。
把两端配成 (1,20)、(3,15)、(4,10)。P 位于每一对的两个端点之间时,到这两个点的距离和恰好等于端点间距;离开这个区间则会更大。这三对距离和至少为 19+12+6=37。
取 P=7 时同时位于三个区间内,且到剩下的中间点 7 的距离为 0,所以恰好达到下界 37。
直接计算也是 6+4+3+0+3+8+13=37,选择 A。这里优化的是绝对距离和,因此用中位数,不是平均数。
一个无向图有 10 个顶点,其中 4 个顶点的度为 3,其余顶点的度均为 4,则该图的边数是( )。
正确答案B
共有 10 个顶点,其中 4 个度数为 3,剩余 10-4=6 个度数为 4,所以全部顶点的度数之和为 。
无向图中的每一条边同时连接两个端点,在这两个端点的度数中各贡献 1,因此把所有度数相加时,每条边恰好被计数两次。
由握手定理,边数为 36/2=18,选择 B。36 是度数总和,不是边数。
程序输入不超过数组或字符串定义的范围;判断题正确填 √,错误填 ×;除特殊说明外,判断题 1.5 分,选择题 3 分,共计 40 分。
1 | #include <iostream>
2 | using namespace std;
3 | int main() {
4 | int n;
5 | cin >> n;
6 | int x = 1, y = 1;
7 | while (n > 0) {
8 | if (n % 2 == 0) {
9 | ++x;
10 | } else {
11 | ++x;
12 | ++y;
13 | }
14 | n = n / 2;
15 | }
16 | cout << x << ' ' << y << endl;
17 | return 0;
18 | }以下问题均假定输入的 为不超过 的非负整数。
(1 分)当输入为 3 时,程序输出为 3 3。( )
正确答案正确
初始 n=3,x=1,y=1。第一轮 n 为奇数,执行 ++x 和 ++y,得到 x=2、y=2;随后整数除法 n=3/2=1。
第二轮 n=1 仍为奇数,得到 x=3、y=3;再令 n=1/2=0。此时 while(n>0) 不再成立,停止循环。
因此输出为 3 3,断言正确。从二进制看,3 写成 11,程序处理两位且两位都是 1,所以两个计数都在初值 1 的基础上增加了 2。
将第 11 行的 ++x; 删除后,程序输出的两个数一定相等。( )
正确答案错误
删除的是奇数分支里的 ++x。修改后,n 为偶数时只有 x 加 1,n 为奇数时只有 y 加 1,两种计数没有必然相等的关系。
以合法输入 n=1 为反例:初始 x=y=1,进入奇数分支后只令 y 变成 2,然后 n 变为 0,循环结束,输出 1 2。
因此“两个数一定相等”错误。对正整数而言,修改后的 x 是初值 1 加上有效二进制表示中 0 的个数,y 是初值 1 加上 1 的个数。
假设输入为非负整数,则程序输出的第一个数一定不小于第二个数。( )
正确答案正确
初始 x=y=1,因此 x-y=0。每轮若 n 为奇数,x、y 同时加 1,差值不变;若 n 为偶数,只有 x 加 1,差值增加 1。
所以 x-y 从不减少,也就始终非负,即 x>=y,断言正确。这个结论直接由两个分支的更新方式得到,不需要枚举所有输入。
还要检查边界 n=0:循环一次也不执行,输出 1 1,仍然满足第一个数不小于第二个数。
将第 7 行的 while (n > 0) 改为 while (n >= 0) 后,程序可能出现的问题是( )。
正确答案A
原程序将正整数 n 不断整除 2,最终变为 0,此时 n>0 为假,能够结束循环。
改成 n>=0 后,n=0 时仍会进入循环;0 是偶数,因此 x 加 1,而 n=0/2 仍为 0。以后循环条件一直成立,n 再也无法离开这个状态。
所以从终止条件的逻辑来看,程序无法正常退出,按本题考查意图选择 A“陷入死循环”;输出语句位于循环之后,不能简单认为只是输出的数变大。
严格按 C++ 语义,x 是有符号 int,持续自增最终会溢出并触发未定义行为,因此不能保证真实程序一定无限运行。这里应区分循环条件的错误与溢出后的具体表现。
当输入为 6 时,输出为( )。
3 34 24 35 2正确答案C
逐轮记录 n、x、y。初始为 n=6、x=1、y=1。
第一轮 n=6 为偶数,只令 x=2,然后 n=3;第二轮 n=3 为奇数,令 x=3、y=2,然后 n=1;第三轮 n=1 为奇数,令 x=4、y=3,然后 n=0 并退出。
最终输出 4 3,选择 C。也可以用二进制核对:6 为 110,共 3 位,其中有 2 个 1;每位都会令 x 增加,而只有 1 位才令 y 增加。
因此 x=1+3=4、y=1+2=3,最高位处理完后不会为 n=0 再执行一轮。
若输入 依次取遍 中的所有整数,则程序输出的第二个数恰好为 2 的次数为( )。
正确答案C
每轮 n%2 检查当前二进制最低位,n/2 则相当于去掉这一位。y 初始为 1,遇到最低位为 1 时才增加,因此最终 y=1+原输入二进制中 1 的个数。
要求 y 恰好为 2,就要求输入恰好有一个二进制位为 1。这些数只能是 2 的整数次幂:1、2、4、8、……。
给定范围最大是 ,所以允许的数为 ,指数从 0 到 30 共 31 个,选择 C。
输入 0 时没有任何 1 位,输出 y=1; 又超过上界,二者都不能计入。
1 | #include <algorithm>
2 | #include <iostream>
3 | #include <string>
4 | using namespace std;
5 | int a[100007], b[100007], c[100007], carry[100007];
6 | string input_str;
7 | int a_len, b_len;
8 | int main() {
9 | cin >> input_str;
10 | a_len = input_str.size();
11 | for (int i = 0; i < a_len; i++) {
12 | a[i] = input_str[a_len - i - 1] - '0';
13 | }
14 | cin >> input_str;
15 | b_len = input_str.size();
16 | for (int i = 0; i < b_len; i++) {
17 | b[i] = input_str[b_len - i - 1] - '0';
18 | }
19 | carry[0] = 0;
20 | for (int i = 0; i < max(a_len, b_len) + 1; i++) {
21 | c[i] = a[i] + b[i] + carry[i];
22 | if (c[i] >= 10) {
23 | carry[i + 1] = 1;
24 | c[i] -= 10;
25 | } else {
26 | carry[i + 1] = 0;
27 | }
28 | }
29 | for (int i = max(a_len, b_len); i >= 0; i--) {
30 | cout << c[i];
31 | }
32 | cout << endl;
33 | return 0;
34 | }本题输入的两个数均为非负整数,位数不超过 100000,可能包含前导零。
当输入为 123 456 时,程序输出为 0579。( )
正确答案正确
读入时把数字倒序存储,a[0..2]={3,2,1}、b[0..2]={6,5,4},下标 0 表示个位,方便从低位向高位处理进位。
逐位相加得到 c[0]=3+6=9,c[1]=2+5=7,c[2]=1+4=5,均不进位。数组是全局数组,更高位初值为 0,因此额外计算的 c[3]=0。
输出循环固定从 max(a_len,b_len)=3 开始,依次输出 c[3]、c[2]、c[1]、c[0],即 0、5、7、9,结果为 0579,断言正确。
本题必须按实际输出循环判断,不能只写数学上的和 579。
假设输入的两个数均不含前导零,则程序输出的结果也一定不会含有前导零。( )
正确答案错误
没有前导零的输入,并不保证输出程序会自动去掉前导零。此程序固定输出“较长输入的位数+1”个数位,没有先检查最高位是否为 0。
例如输入 123 和 456,两个输入都不含前导零。相加得到 579,没有向千位进位,但程序仍从 c[3] 输出到 c[0],得到 0579。
这个反例满足题目条件,却产生了前导零,因此断言错误。只有相加确实产生最高位进位时,多出的这一位才可能为 1。
将第 21 行改为 c[i] = a[i] + b[i]; 后,程序输出的结果一定比原来的结果小。( )
正确答案错误
carry[i] 表示低一位传来的进位。删去它会使需要进位的加法出错,但不意味着每一组输入都会改变结果。
若各位相加都小于 10,就没有任何进位,carry[i] 原本全部为 0。例如 123+456 中,个位 3+6=9、十位 2+5=7、百位 1+4=5,都不进位。
因此删除 carry[i] 前后输出同样都是 0579,并没有变小,足以否定“一定比原来小”,所以断言错误。
判断带有“一定”的命题时,一个满足条件且结果相等的反例就够了,不必证明所有可能输入的大小关系。
当输入为 12345 678 时,输出为( )。
01292301302313023130230正确答案B
倒序存储后,从个位逐位计算,并把上一位的进位加入本位。
个位:5+8=13,写 3、进 1;十位:4+7+1=12,写 2、进 1;百位:3+6+1=10,写 0、进 1;千位:2+0+1=3,不进位;万位:1+0=1。
因此 c[0..4] 依次为 3、2、0、3、1,数学上的和是 13023。较长输入有 5 位,程序还计算 c[5]=0,并从下标 5 一直输出到 0。
最终字符串为 013023,选择 B。选项 13023 漏掉了程序实际输出的最高位 0。
左右滑动表格,可查看完整内容。
| 数位 | 本位相加(含进位) | 保留数位 | 向高位进 |
|---|---|---|---|
| 个位 | 5 + 8 = 13 | 3 | 1 |
| 十位 | 4 + 7 + 1 = 12 | 2 | 1 |
| 百位 | 3 + 6 + 1 = 10 | 0 | 1 |
| 千位 | 2 + 0 + 1 = 3 | 3 | 0 |
| 万位 | 1 + 0 = 1 | 1 | 0 |
| 额外最高位 | 0 + 0 = 0 | 0 | 0 |
计算从低位到高位;输出方向相反。把“保留数位”一列倒过来读,就是 013023。
将第 22 行的 if (c[i] >= 10) 改为 if (c[i] > 10) 后,当输入为 95 15 时,输出为( )。
010101101401410正确答案A
修改后只有 c[i]>10 才进位,恰好等于 10 时不会进位,也不会减去 10。
输入 95、15:个位 c[0]=5+5+0=10,不满足 >10,所以保留整数 10,carry[1]=0;十位 c[1]=9+1+0=10,同样保留 10,carry[2]=0;额外的百位 c[2]=0。
输出顺序是 c[2]、c[1]、c[0]。cout 输出的是每个数组元素的整数值,因此依次输出字符片段“0”“10”“10”,拼接成 01010,选择 A。
程序此时已不再保证每个 c[i] 都在 0 到 9 之间,所以不能把三个数组元素误当成三个单字符数位。
假设输入的两个数均为 位正整数(不含前导零),且它们的和小于 ,则程序输出的字符串一定满足( )。
正确答案C
两个输入的长度均为 n,因此程序固定计算并输出下标 n 到 0,一共 n+1 个数组元素。
正常的 >=10 进位判断保证每个输出元素都在 0 到 9 之间,所以每个元素恰好输出一个字符,整个字符串长度就是 n+1。
又因为题目保证和小于 ,最高的第 n 位没有进位,c[n]=0;两个 n 位正整数的和又至少是 n 位,所以这是额外补在最前面的 0。
因此输出一定长 n+1,首字符为 '0',选择 C。数值位数与程序输出字符串的长度需要分开考虑。
1 | #include <iostream>
2 | using namespace std;
3 | bool check_prime(int x) {
4 | if (x <= 1) return false;
5 | for (int i = 2; i * i <= x; i++) {
6 | if (x % i == 0) return false;
7 | }
8 | return true;
9 | }
10 | int n;
11 | void search_result(int x) {
12 | if (!check_prime(x)) return;
13 | if (x >= n) {
14 | cout << x << endl;
15 | return;
16 | }
17 | for (int i = 0; i <= 9; i++) {
18 | search_result(x * 10 + i);
19 | }
20 | }
21 | int main() {
22 | cin >> n;
23 | for (int i = 1; i <= 9; i++) search_result(i);
24 | return 0;
25 | }当输入为 10 时,程序的输出共有 10 行。( )
正确答案错误
search_result 先判断 x 是否为质数,不是就立即返回。因此主函数的 1 到 9 中,只有 2、3、5、7 会继续扩展。
n=10 时,这四个一位质数都小于 10,会在末尾尝试添加一个数字;生成的两位质数已经达到阈值,输出后立即停止,不再生成三位数。
以 2 开头得到 23、29;以 3 开头得到 31、37;以 5 开头得到 53、59;以 7 开头得到 71、73、79。共 2+2+2+3=9 行。
所以“共有 10 行”错误。像 11、13 虽然也是两位质数,但首位 1 不是质数,在最初就被剪枝,不会生成。
若输入的 不大于 5,则程序的输出中一定包含 5。( )
正确答案正确
主函数依次调用 search_result(1) 到 search_result(9),其中 search_result(5) 是一个独立的调用,不依赖其他分支是否产生输出。
进入 search_result(5) 后,check_prime(5) 为真;若 n<=5,则 x>=n 也成立,程序立即输出 5,然后结束这个分支。
因此输出中一定包含 5,断言正确。return 只结束当前函数调用,不会让主函数停止遍历后面的一位数。
若输入的 大于 10,将第 17 行的 for (int i = 0; i <= 9; i++) 改为 for (int i = 1; i <= 9; i += 2) 后,程序的输出结果一定不变。( )
正确答案正确
只有质数 x 能执行扩展循环,而最小的一位质数是 2,所以新生成的 x*10+i 至少为 20,是一个多位数。
如果 i 为 0、2、4、6、8,新数的个位是偶数,且新数大于 2,因此一定是合数。原程序进入这些递归调用后,也会在 check_prime 检查处立即返回,不产生任何输出和后续分支。
改成 i=1、3、5、7、9,只是跳过这些必定失败的偶数末位分支;保留分支的相对顺序没有变化,故输出内容及顺序都不变,断言正确。
一位质数 2 不能作为反例,因为主函数对 2 的初始调用并未删除。
当输入为 24 时,程序输出的第 3 行为( )。
正确答案B
n=24 时,先从一位质数 2 的分支开始。20、21、22 不是质数,跳过;23 是质数但 23<24,不能直接输出,必须继续深入它的子分支。
在 230 到 239 中,质数只有 233、239。它们均不小于 24,所以依次输出,成为第 1 行和第 2 行。
23 的分支处理完后,返回 2 的扩展循环,继续检查 24、25、……、29。只有 29 是质数且达到阈值,于是第 3 行输出 29,选择 B。
这说明深度优先搜索会先完成当前前缀的全部工作,再处理下一个同层前缀,所以输出不是按数值从小到大排序。
n = 24。只画相关质数分支,合数调用省略。输出顺序是 233 → 239 → 29,而不是数值升序。
下列关于该程序输出的说法中,正确的是( )。
正确答案D
每个多位数都是由某次调用中的 x 通过 x*10+i 生成的。程序在扩展前先检查 x 是否为质数;只有检查通过,才会尝试添加末位。
因此,某个多位数如果被输出,删去它的末位就得到生成它的父状态 x,而这个父状态必定是质数,选择 D。沿递归调用链继续向上,还能知道它的每个非空十进制前缀都为质数。
不要把深度优先顺序误认为数值升序:n=24 时先输出 233、239,再输出 29,已经不是从小到大。
也不能认为阈值越大输出越少:n=10 时有 9 行,而 n=200 时有 14 行;提高阈值会使原本停止的分支继续扩展,可能产生更多输出。
当输入为 200 时,程序输出的行数为( )。
正确答案C
n=200 时,所有一位和两位质数都小于阈值,程序会继续扩展。可到达的两位质数前缀为 23、29、31、37、53、59、71、73、79。
分别检查在这些前缀后添加一位得到的质数:23 → 233、239;29 → 293;31 → 311、313、317;37 → 373、379;53 → 无。
其余分支为:59 → 593、599;71 → 719;73 → 733、739;79 → 797。
这些三位数均不小于 200,输出后立即返回,不再继续扩展。因此总行数为 2+1+3+2+0+2+1+2+1=14,选择 C。
不能直接统计所有三位质数,因为程序只会到达每一级前缀都为质数的数。
单选题,每小题 3 分,共计 30 分。
给定 ,再给定一个 进制下的数 ,其各个数位上的数按照从高位到低位的顺序给出,请你将其转化为 进制,并同样按照从高位到低位的顺序输出。
输入的第一行依次为 和 的位数 ,接下来 个数 从高位到低位描述各个数位上的数。
数据满足 ,,;对于所有 ,。
以下程序按“逐位除以 ”的方法完成进制转换。请补全程序。
#include <iostream>
constexpr int N = 100005;
long long b[N];
int main() {
long long n, m, d;
std::cin >> n >> m >> d;
int len = 1;
for (int i = 0; i < d; i++) {
long long x;
std::cin >> x;
for (int j = len; j >= 1; j--)
b[j] = /* ① */;
b[0] = /* ② */;
len++;
for (int j = 0; j < len; j++)
if (b[j] >= n) {
b[j + 1] += /* ③ */;
b[j] = /* ④ */;
if (j + 1 == len) len++;
}
}
while (/* ⑤ */) len--;
for (int i = len - 1; i >= 0; i--)
std::cout << b[i] << ' ';
return 0;
}①处应填( )。
b[j] * nb[j] * mb[j - 1] * nb[j - 1] * m正确答案D
先理解整个转换过程:按高位到低位读入原数时,每读到一个新数位 x,旧数值 V 都应更新为 V*(mn)+x。例如十进制逐位读入 1、2、3,就是不断执行 V=V*10+x。
这里 b 用 n 进制、低位在前存储,。把旧 b[j-1] 放到新 b[j],相当于把每一位的权值乘 n;还需要把系数乘 m,整体才是乘 mn。
所以①应为 b[j-1]*m,选择 D。循环从高下标向低下标执行,能保证读取的 b[j-1] 仍是旧值,不会被刚写入的数据覆盖。
例如 n=2、m=3,旧值 1 的数组低位为 [1],移位并乘 3 后暂为 [0,3],代表 0+3×2=6,正好是旧值乘原进制 6;随后再统一进位。
②处应填( )。
x * nx0m正确答案B
上一段循环已经把旧值乘 mn 的贡献放到下标 1 及更高的位置,相当于在目标 n 进制中腾出了最低位。
新的数值还需要加上当前读入的原进制数位 x,因此应把 b[0] 设为 x,选择 B。下标 0 的权值是 ,所以写入 x 就贡献数值 x,不需要再乘 n。
注意 x 的范围是 0<=x<mn,可能大于等于 n,暂时不是一个合法的 n 进制数位;后面的进位循环会把它规范化。
例如 n=2、m=3、x=5,先写 b[0]=5,再逐级进位可得到低位在前的 [1,0,1],即二进制 101。
③处应填( )。
b[j] / mb[j] % nb[j] % mb[j] / n正确答案D
目标是 n 进制,每 n 个当前位的单位可以换成高一位的 1 个单位。若当前 b[j]=t,根据带余除法有 。
所以向高位传递的数量是整数商 b[j]/n,应执行 b[j+1]+=b[j]/n,选择 D;本位剩余量则在下一空中处理。
例如 n=3、当前位为 8,就应向高一位进 2,本位留下 2,因为 8=2×3+2。
这里必须用 +=,因为高一位可能已经有“旧数值乘 mn”产生的贡献;进位只是追加的贡献,不能把原有值覆盖。
④处应填( )。
b[j] / mb[j] % nb[j] % mb[j] / n正确答案B
将 b[j]/n 个单位进到高位之后,本位应只留下不足 n 的部分,也就是 b[j]%n,选择 B。
仍以 n=3、原 b[j]=8 为例:向高位加 2,本位改为 2,数值保持不变,同时当前位已经落入合法范围 0 到 n-1。
进位循环从低位到高位处理,因此高位接收了低位进位后,还会继续接受检查;必要时可以产生连锁进位,直到每一位都合法。
整数商与余数承担不同职责:商决定“向高位进多少”,余数决定“本位留多少”,不能把两者互换。
⑤处应填( )。
len > 0 && b[len - 1] == 0len > 0 && b[0] == 0len > 1 && b[len - 1] == 0len > 1 && b[0] == 0正确答案C
数组 b 按低位到高位存储,因此当前最高位是 b[len-1],不是 b[0]。逐位转换过程中 len 可能多预留了零位,输出前需要从最高位开始删除这些多余的 0。
每次只有在 len>1 且 b[len-1]==0 时才缩短一位,所以条件为 len>1 && b[len-1]==0,选择 C。
len>1 保证数值 0 至少保留一个数位;若写 len>0,全部为 0 时可能把 len 减到 0,导致输出循环不输出任何数位。
也不能检查 b[0]:例如二进制 100 的最低位是 0,但它是合法的数值组成部分,不是应删除的前导零。
给定一个长度为 的字符串,其中每个字符都是一个十六进制数位。例如,字符串 016A 表示十进制下的四个数 。
现在请选择 个( 是你选定的数)切分位置 ,其中 ,且 。再令 ,。
对于每个 ,计算第 个数到第 个数的平均值,记作 。你的目标是使 中最大值与最小值之差尽可能小,并输出这个最小值。
其中 。输入字符串中的字符只可能是 0—9 或 A—F。本题假定字符采用 ASCII 编码。输出答案时保留小数点后 6 位。
以下程序通过递归枚举所有可能的连续分段方案。请补全程序。
#include <algorithm>
#include <iomanip>
#include <iostream>
using namespace std;
constexpr int N = 25;
int n, a[N];
char s[N];
double ans = 1e100;
int value(char c) { return /* ① */; }
void split(int l, int cnt, double mnb, double mxb) {
if (l > n) {
if (cnt == 0) return;
ans = min(ans, mxb - mnb);
return;
}
int sum = 0;
for (/* ② */) {
sum += a[r];
double nwb = /* ③ */;
split(/* ④ */);
}
}
int main() {
cin >> n >> s + 1;
for (int i = 1; i <= n; ++i)
a[i] = value(s[i]);
split(/* ⑤ */);
cout << fixed << setprecision(6) << ans;①处应填( )。
c - (c < '9' ? '0' : 'A' - 10)c - (c < 'A' ? '0' : 'A' - 10)c - (c < 'A' ? 'A' - 10 : '0')c - (c < 'A' ? '0' : 'A' + 10)正确答案B
输入只可能是 '0'—'9' 或 'A'—'F',且题目指定 ASCII。数字字符的编码都小于 'A',因此可用 c<'A' 判断当前是哪一类。
若是数字字符,数值为 c-'0',例如 '6'-'0'=6。若是字母字符,数值为 c-'A'+10,例如 'A' 对应 10、'F' 对应 15。
把两种减数统一放入条件表达式,就得到 c-(c<'A' ? '0' : 'A'-10),选择 B。注意减去 ('A'-10) 正好等于先减 'A' 再加 10。
若使用 c<'9' 分类,字符 '9' 不满足严格小于,会被误送入字母分支,因此边界不正确。
②处应填( )。
int r = l + 1; r <= n; ++rint r = l; r < n; ++rint r = l; r <= n; r += 2int r = l; r <= n; ++r正确答案D
split(l,...) 表示前面的区间已经分好,现在从下标 l 开始选择新的一段。为了枚举所有非空连续段,右端点 r 可以是 l、l+1、……、n 中任意一个。
因此循环应为 int r=l; r<=n; ++r,选择 D。每轮先执行 sum+=a[r],此时 sum 恰好是当前段 a[l..r] 的元素和。
从 r=l 开始可以包含只有一个元素的段;允许 r=n 才能包含最后一段,并让递归最终到达 l=n+1 的结束状态。
例如剩余下标为 3、4、5,当前段必须考虑 [3,3]、[3,4]、[3,5] 三种长度,不能跳过任何一种。
③处应填( )。
sum / (r - l + 1) * 1.0sum * 1.0 / (r - l) + 1sum * 1.0 / (r - l + 1)(sum - a[r]) * 1.0 / (r - l + 1)正确答案C
当前段是闭区间 [l,r],共有 r-l+1 个元素,而 sum 已经累计了这段所有元素,因此平均值是 sum/(r-l+1)。
但 sum 与区间长度都是整数,直接相除会先执行整数除法,丢失小数部分。因此必须在除法前把一个操作数转为浮点数,应写 sum*1.0/(r-l+1),选择 C。
例如当前段为 {1,2},正确均值是 3/2=1.5。若写 sum/(r-l+1)*1.0,会先算整数商 1,再乘 1.0,结果仍然只有 1.0。
分母不能写 r-l,否则单元素段 r=l 时分母为 0;也不能从 sum 中减去 a[r],因为右端点本来就属于当前段。
④处应填( )。
r + 1, cnt + (r < n), min(mnb, nwb), max(mxb, nwb)r + 1, cnt + (r <= n), min(mnb, nwb), max(mxb, nwb)r + 1, cnt + (r < n), max(mnb, nwb), min(mxb, nwb)r + 1, cnt + (r <= n), max(mnb, nwb), min(mxb, nwb)正确答案A
当前选择的段为 [l,r],下一段必须从 r+1 开始,这样各段才能首尾相接,不重叠也不遗漏。
cnt 记录的是切分位置的个数,而不是段数。只有 r<n 时,当前段后面还有元素,在 r 与 r+1 之间才形成一个切分位置;r=n 时只是整串的结束,不应增加 cnt。所以应传 cnt+(r<n)。
加入当前段的平均值 nwb 后,所有已选段的最小均值更新为 min(mnb,nwb),最大均值更新为 max(mxb,nwb)。四个参数合起来就是选项 A。
例如 n=3,若第一段直接选 [1,3],下一次 l=4、cnt=0,终止分支会拒绝它,因为题目要求至少切一次。若误写 cnt+(r<=n),这个完全不切的方案会被接受,并以最大值等于最小值的 0 错误压低答案。
中间边界 r = 2 < n:cnt 加 1;末尾 r = n:不增加切分数。
示例字符串 016A。切成 [0,1] 与 [6,10],均值分别为 0.5、8,差为 7.5。这是一个枚举方案,不表示最优解。
⑤处应填( )。
0, 0, 1e100, -1e1000, 0, -1e100, 1e1001, 0, -1e100, 1e1001, 0, 1e100, -1e100正确答案D
输入保存在 s+1,随后也把 a[1] 到 a[n] 赋值,因此第一次应从 l=1 开始。此时尚未放置任何切分位置,所以 cnt=0。
还没有选中任何段时,最小均值应设成很大的正数 1e100,最大均值应设成很小的负数 -1e100。这样加入第一段均值 v 后,min(1e100,v)=v,max(-1e100,v)=v,两个状态都正确变成 v。
所以初始参数是 1,0,1e100,-1e100,选择 D。若极值方向反过来,后面的 min/max 更新无法把它们替换成实际均值。
从整体上看,n-1 个相邻位置各有“切或不切”两种选择,共 个分段方案;程序用 cnt==0 排除完全不切的一种,再对其余方案的“最大段均值-最小段均值”取最小值。