前言
本文中的题干来自于 洛谷,祝大家取得理想成绩!文中的选项顺序与真实考试会有不同,但内容相同。
如有疏漏,还望指出。如果你喜欢,请留下宝贵的赞,谢谢。
文章将模拟考试思路,在阅读解法的同时,希望读者可以学习答题的方法,并且有所收获。
正文
选择题(共 15 题,每题 2 分,共计 30 分)
第一题
下列 C++ 数据类型中,能够精确存储 10 18 + 1 10^{18}+1 1018+1 这个整数的是( )。
A.
floatB.long longC.doubleD.int答案:B
首先看到关键词"整数",首先排除 A float 和 B double,这两者用于存储小数;int 的范围约为 21 亿(即 − 2 31 -2^{31} −231 到 2 31 − 1 2^{31}-1 231−1),约 10 9 10^9 109,所以不选 D int。答案选 B long long。
long long 的范围: − 2 63 -2^{63} −263 到 2 63 − 1 2^{63}-1 263−1,答案正确。
第二题
十六进制数
2F5转换为八进制数是( )。A.
1364B.1635C.1405D.1365答案:D
采取思路:十六进制→十进制→八进制。
这里可以参考我的文章《进制转化入门》,上面有更详细的讲解。
首先十六进制转为十进制 ,从最低位开始,依次乘以 16 i 16^i 16i,其中 i 为位数,从 0 0 0 开始;十六进制中,大于 10 10 10 的数用字母表示,所以 F 代表 15 15 15。那么这个数转换为十进制,列式: 5 × 16 0 + 15 × 16 1 + 2 × 16 2 = 757 5\times 16^0+15\times 16^1+2\times 16^2=757 5×160+15×161+2×162=757。即:
2F5 = 2×16² + F×16¹ + 5×16⁰
= 2×256 + 15×16 + 5×1
= 512 + 240 + 5
= 757
然后将 757 757 757 转为八进制,用短除法取余数。
757 ÷ 8 = 94 ... 余 5
94 ÷ 8 = 11 ... 余 6
11 ÷ 8 = 1 ... 余 3
1 ÷ 8 = 0 ... 余 1
因此答案为 1365 1365 1365。选 D。
第三题
执行下列 C++ 代码,输出是( )。
A. 9 B. 10 C.7 D.6
cppint a = 7, b = 3; std::cout << a / b * b + a % b;答案:C。
手动模拟即可。注意当 a a a 除以 b b b 除不尽时,取整数部分(向下取整)。
原式 = 7 / 3 × 3 + 7 % 3 7/3\times 3+7\% 3 7/3×3+7%3
= 2 × 3 + 1 2\times 3+1 2×3+1
= 7 7 7
答案选 C。
第四题
初始时栈为空,将 1、2、3、4 依次入栈,入栈过程中允许随时出栈。下列出栈序列中不可能出现的是( )。
A. 2,4,3,1
B. 1,2,3,4
C. 3,1,2,4
D. 1,4,3,2
答案:C
手动模拟,注意"过程中允许随时出栈"。
只需要模拟一下选项即可。下面给出 A 选项的例子:
【栈】:(空)
入栈1、2
【栈】:1 2
出栈2
【栈】:1
入栈3、4
【栈】:1 3 4
出栈 4、3、1
栈空,且合法,所以 A 选项正确。
其他选项不再赘述。
第五题
一棵有 100 个结点的完全二叉树,其叶子结点个数是( )。
A. 49 B. 50 C. 64 D. 51
答案:B
完全二叉树,是指二叉树仅最后一层可能有空,且空位靠右的树。举个例子:
下图(图1)不是完全二叉树。

下图(图二)是完全二叉树,同时也是一棵满二叉树。

下图(图三)不是完全二叉树,因为最底层节点并不是全部靠左。

下图(图四)是一棵完全二叉树。

回到题目,因为这棵完全二叉树有 100 100 100 个节点,那么它上层已满的节点数为 1 + 2 1 + 2 2 + 2 3 + 2 4 + 2 5 = 63 1+2^1+2^2+2^3+2^4+2^5=63 1+21+22+23+24+25=63 个节点。为什么加到 2 5 2^5 25,因为如果加到 2 6 2^6 26 就会大于 100 100 100 个节点。
说明最后一层的叶子节点有 37 37 37 个,加上上一层仍是叶子(因为没有孩子)的节点 32 − ( 37 + 1 ) / 2 − 1 = 13 32-(37+1)/2-1=13 32−(37+1)/2−1=13 个,答案为 50 50 50 个。选 B。
第六题
执行下列代码后 s 的值是( )。
cppint s = 0; for (int i = 1; i <= 100; i++) if (i % 3 == 0 || i % 5 == 0) s += i;A. 3048 B. 2733 C. 2318 D. 2418
答案:D
程序是在求, 1 1 1 到 100 100 100 之间,所有是 3 3 3 或 5 5 5 的倍数的数之和。
数学题,只需利用等差数列公式求出 3 3 3 和 5 5 5 所有的倍数和,减去 15 15 15 的倍数和(重叠部分)即可。
答案选 D。
第七题
上楼梯每步可上 1 级、2 级或 3 级,从地面(可视为第 0 级)走到第 8 级台阶共有多少种不同走法( )。
A. 44 B. 121 C. 149 D. 81
答案:D
经典的递归题,我们设 f ( x ) f(x) f(x) 为走到第 x x x 个台阶时的走法数,其中 f ( 1 ) = 1 , f ( 2 ) = 2 , f ( 3 ) = 4 f(1)=1,f(2)=2,f(3)=4 f(1)=1,f(2)=2,f(3)=4。对于任意的 i ( i > 3 ) i(i>3) i(i>3),有 f ( i ) = f ( i − 1 ) + f ( i − 2 ) + f ( i − 3 ) f(i)=f(i-1)+f(i-2)+f(i-3) f(i)=f(i−1)+f(i−2)+f(i−3)。下文解释为什么这样子列式:
首先是一至三阶的情况,我们可以直接算出;对于三阶以上,我们可能从前一阶、两阶或三阶的位置上来,对应递归式。
带入 f ( 8 ) = 81 f(8)=81 f(8)=81,答案选 D。
第八题
下图为 5×5 网格,行号、列号均从 0 开始,
#为障碍,.为可通行格:
S..#. ...#. ...#. ##..E ...#.从 S 出发做广度优先搜索(BFS):初始时把 S 入队;每次取出队首格子,按"上、下、左、右"(上 = 行号减 1,下 = 行号加 1,左 = 列号减 1,右 = 列号加 1)的顺序遍历它的四个相邻格子,越界、障碍或已访问的格子跳过,其余格子标记为已访问并入队。当 E 第一次入队时,已经入队过的格子(含 S 和 E)共有多少个( )。
A. 15 B. 12 C. 14 D. 13
答案:C
根据题意手动模拟即可。从开始处,依次走其"上下左右"方向,并且做记录,再从记录的地方继续往四个方向走。答案就是记录的数量(遇到的每一个节点)。
答案选 C。
第九题
满足 1 ≤ n ≤ 100 1\leq n\leq 100 1≤n≤100 且 g c d ( n , 60 ) = 6 gcd(n,60)=6 gcd(n,60)=6 的正整数 n n n 共有多少个( )。
A. 8 B. 6 C. 4 D. 5
答案:B
g c d ( x , y ) gcd(x,y) gcd(x,y) 意为 x x x 与 y y y 的最大公因数。范围不大,枚举即可,答案选 B。
第十题
某国硬币面值为 1 元、4 元、6 元且数量不限,凑出 9 元最少需要多少枚( )。
A. 3 B. 4 C. 5 D. 2
答案:A
整套卷子最简单的题,没有之一,一年级数学题。选法 4 + 4 + 1 = 9 4+4+1=9 4+4+1=9,三枚,对应选 A。
第十一题
执行下列代码,输出是( )。
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];A.
14,13B.8,13C.14,7D.14,2答案:A
*p = a + 2,p 指向 a[2],此时 p0=a2=5;
*(p - 1) = p[0] + p[2];,*(p-1) 指向 a1,p2 指向 a2+2 (a4)=7。所以此时 a1=5+7=14;
p[1] = *(a + 1) - a[0];,p[1] 是 a3,*(a + 1) 就是 a1 当前的值为 14,a0 是 1,于是 a3 = 13。
因此答案选 A(14,13)。
第十二题
在含 1000 个互不相同元素的升序数组中,用二分法查找给定值(返回元素位置或报告不存在),最坏情况下需要与数组元素比较多少次( )。
A. 500 B. 9 C. 11 D. 10
答案:D
对于一个含有 n n n 个元素的有序数组,二分查找最坏情况下比较次数为: ⌊ log 2 n ⌋ + 1 \lfloor \log_2 n \rfloor +1 ⌊log2n⌋+1
带入 n = 100 n=100 n=100,得数为 10 10 10,答案选 D。
解释公式:
因为每一次二分查找,区间大小变为 ⌊ n / 2 ⌋ \lfloor n/2 \rfloor ⌊n/2⌋,对应 ⌊ l o g 2 n ⌋ \lfloor log_2 n\rfloor ⌊log2n⌋。又因为最后还有一次比较(区间大小为 1 1 1 时,检测查找的元素是否存在),所以总共是 ⌊ log 2 n ⌋ + 1 \lfloor \log_2 n \rfloor +1 ⌊log2n⌋+1 次。
第十三题
数组 a 1... n a1...n a1...n 的前缀和数组 S S S(即 S i = a 1 + a 2 + . . . + a i Si=a1+a2+...+ai Si=a1+a2+...+ai)满足 S i = 3 i 2 + i Si=3i^2+i Si=3i2+i。则 a 10 a10 a10 的值是( )。
A. 252 B. 310 C. 58 D. 61
答案:C
根据前缀和数组的定义,我们可以得出 a 10 = S 10 − S 9 a10=S10-S9 a10=S10−S9。因此,只需算出 S 10 S10 S10 和 S 9 S9 S9 的值,即可得到答案。
即 310 − 252 = 58 310 - 252 = 58 310−252=58,答案选 C。
第十四题
数轴上有 7 个点,坐标分别为 1、3、4、7、10、15、20。在数轴上选取一个整数坐标点 P,使 P 到这 7 个点的距离之和最小,这个最小距离和是( )。
A. 37 B. 42 C. 40 D. 38
答案:A
在考场上没有巧妙的解法时,因为数据不大,也可以依次去试(答案依然正确)。下文是中位数的做法:
在数轴上,到若干点距离之和最小的点,是这些点的中位数。
-
如果点的个数是奇数,中位数就是排序后中间那个数。
-
如果是偶数,中间两个数之间的任意点(包括这两个点)都能使距离和最小。
因此取第四个数( 7 7 7)时,距离和最小,为 6 + 4 + 3 + 0 + 3 + 8 + 13 = 37 6+4+3+0+3+8+13=37 6+4+3+0+3+8+13=37,对应答案选 A。
第十五题
一个无向图有 10 个顶点,其中 4 个顶点的度为 3,其余顶点的度均为 4,则该图的边数是( )。
A. 36 B. 18 C. 17 D. 20
答案:B
根据(小学)握手原理,无向图中,所有顶点的度数之和 = 边数的 2 2 2 倍。设边数为 E E E,则:
∑ 度 = 2 E \sum 度 = 2E ∑度=2E
因此解此题非常简单,总度数和为 4 × 3 + 6 × 4 = 12 + 24 = 36 4\times 3+6\times 4=12+24=36 4×3+6×4=12+24=36,边数为 36 / 2 = 18 36/2=18 36/2=18。因此答案选 B。
阅读程序(共三题)
程序输入不超过数组或字符串定义的范围;判断题正确填 √,错误填 ×;除特殊说明外,判断题 1.5 分,选择题 3 分,共计 40 分。
(1)
cpp
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int x = 1, y = 1;
while (n > 0) {
if (n % 2 == 0) {
++x;
} else {
++x;
++y;
}
n = n / 2;
}
cout << x << ' ' << y << endl;
return 0;
}
输入的 n n n 为不超过 2 31 − 1 2^{31}-1 231−1 的非负整数。
代码功能
统计整数 n n n 的二进制表示中总位数 和 1 1 1 的个数 ,并分别加 1 1 1。
即:
- x = n x = n x=n 的二进制位数 + 1 + 1 +1;
- y = n y = n y=n 的二进制中 1 1 1 的个数 + 1 + 1 +1。
例,当 n = 5 n=5 n=5( 1010 1010 1010) 时,
- x = 5 x=5 x=5;
- y = 3 y=3 y=3。
原因:
-
每循环一次, x x x 一定加 1。
-
只有当前位是 1 1 1(即 n n n 为奇数)时, y y y 才加 1 1 1。
-
循环次数 = n = n =n 的二进制位数。
而循环每次缩小到 ⌊ n / 2 ⌋ \lfloor n/2\rfloor ⌊n/2⌋。
可自行模拟验证。
判断题
- (1 分)当输入为 3 时,程序输出为
3 3。( )答案:✔
3 的二进制: x = 2 + 1 = 3 x=2+1=3 x=2+1=3, y = 2 + 1 = 3 y=2+1=3 y=2+1=3。
- 将第 11 行的
++x;删除后,程序输出的两个数一定相等。( )答案:❌
删除后的逻辑:
- 当前位是 0 0 0(偶数)时:
x+1; - 当前位是 1 1 1(奇数)时:
y+1。
最终:
x = 1 + 二进制中 0 的个数;y = 1 + 二进制中 1 的个数。
当 n n n 的二进制中 0 0 0 和 1 1 1 的数量不相等时,x 不等于 y。所以答案错误。
- 假设输入为非负整数,则程序输出的第一个数一定不小于第二个数。( )
答案:✔
根据我们前面的分析,说法一定正确。
选择题
将第 7 行的
while (n > 0)改为while (n >= 0)后,程序可能出现的问题是( )。A. 陷入死循环
B. 输出结果比原来大
C. 输出结果比原来小
D. 输出结果不受影响
答案:A
当 n n n 变成 0 0 0 以后,会再次进入循环, n / 2 n/2 n/2 仍然等于 0 0 0,永远无法退出循环。所以选 A(陷入死循环)。
- 当输入为 6 时,输出为( )。
A.3 3B.4 2C.4 3D.5 2答案:C
6 6 6 的二进制: 110 110 110,因此 x = 3 + 1 = 4 x = 3 + 1 = 4 x=3+1=4, y = 2 + 1 = 3 y = 2 + 1 = 3 y=2+1=3。对应答案选 C(4 3).
若输入 n 依次取遍 0 , 1 , 2 , . . . , 2 31 − 1 0,1,2,...,2^{31}-1 0,1,2,...,231−1 中的所有整数,则程序输出的第二个数恰好为 2 的次数为( )。
A. 16 B. 30 C. 31 D. 32
答案:C
当 y = 2 y = 2 y=2 时, n n n 的二进制表示中恰好有 1 1 1 个 1 1 1。
而从 0 0 0 到 2 31 − 1 2^{31} - 1 231−1 中,共有 2 31 2 ^ {31} 231 个整数,每个整数都可以用 31 31 31 位二进制表示。因此这个 1 1 1 可能出现在 0 , 1 , . . . , 30 0,1,...,30 0,1,...,30(共 31 31 31 个)处的任意一位。
因此共有 31 31 31 个这样的数,答案选 C。
(2)
cpp
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int a[100007], b[100007], c[100007], carry[100007];
string input_str;
int a_len, b_len;
int main() {
cin >> input_str;
a_len = input_str.size();
for (int i = 0; i < a_len; i++) {
a[i] = input_str[a_len - i - 1] - '0';
}
cin >> input_str;
b_len = input_str.size();
for (int i = 0; i < b_len; i++) {
b[i] = input_str[b_len - i - 1] - '0';
}
carry[0] = 0;
for (int i = 0; i < max(a_len, b_len) + 1; i++) {
c[i] = a[i] + b[i] + carry[i];
if (c[i] >= 10) {
carry[i + 1] = 1;
c[i] -= 10;
} else {
carry[i + 1] = 0;
}
}
for (int i = max(a_len, b_len); i >= 0; i--) {
cout << c[i];
}
cout << endl;
return 0;
}
代码功能
高精度加法(即两个非负的大数相加,转化成字符串模拟竖式),carry 用于处理进位。
关于本题前导零:
当两个数最高位相加没有产生进位时,输出会多一个前导 0;否则没有前导 0。
这个前导零是为了防止最高位相加后的进位问题。
判断题
- 当输入为
123 456时,程序输出为0579。( )答案:✔
123 + 456 = 579 123+456=579 123+456=579,且最高位( 1 1 1 和 4 4 4)没有进位,所以含有前导零,为 0579 0579 0579,正确。
- 假设输入的两个数均不含前导零,则输出的结果也一定不会含有前导零。( )
答案:❌
根据我们的分析,前导零仅与最高位的进位有关,与输入的数无关。前一题就是一个反例。因此该说法错误。
- 将第 21 行改为
c[i] = a[i] + b[i];后,程序输出的结果一定比原来的结果小。( )答案:❌
修改后的含义为:仍然做加法,但是不加进位。
当计算时没有产生任何进位时,结果仍然等于原来的值。如输入 12 34,输出仍为 046。因此答案错误。
应改为"程序输出的结果小于等于原来的结果"。
假设输入的两个数均为 n n n 位正整数(不含前导零),且它们的和小于 10 n 10^n 10n ,则程序输出的字符串一定满足( )。
A. 第一个字符一定不为
0。B. 长度一定为 n n n。
C. 长度一定为 n + 1 n+1 n+1,且第一个字符为
0。D. 长度可能为 n + 2 n+2 n+2。
答案:C
据题意,输出字符串长度 = n + 1 =n+1 =n+1,且第一个字符是 0 0 0。因此答案选 C。
(3)
cpp
#include <iostream>
using namespace std;
bool check_prime(int x) {
if (x <= 1) return false;
for (int i = 2; i * i <= x; i++) {
if (x % i == 0) return false;
}
return true;
}
int n;
void search_result(int x) {
if (!check_prime(x)) return;
if (x >= n) {
cout << x << endl;
return;
}
for (int i = 0; i <= 9; i++) {
search_result(x * 10 + i);
}
}
int main() {
cin >> n;
for (int i = 1; i <= 9; i++) search_result(i);
return 0;
}
代码功能
check_prime:检查 x x x 是否是质数;
search_result:构造一条类似"素数链"的输出;
main(主函数):从 1 1 1 到 9 9 9 开始尝试。
选择题
- 当输入为 10 时,程序的输出共有 10 行。( )
答案:❌
可以手动模拟,以下展示 DeepSeek 的模拟过程:




因此输出共 2 + 2 + 2 + 3 = 9 2+2+2+3=9 2+2+2+3=9 行,不是 10 10 10 行,答案错误。
- 若输入的 n n n 不大于 5,则程序的输出中一定包含 5。( )
答案:✔
5 是素数,输出中一定包含,因此正确。
- 若输入的 n 大于 10,将第 17 行的
for (int i = 0; i <= 9; i++)改为for (int i = 1; i <= 9; i += 2)后,程序的输出结果一定不变。( )答案:✔
跳过了数字 2 , 4 , 6 , 8 2,4,6,8 2,4,6,8,它们都不会延伸出新的输出。对于 2 2 2,跳过的是 0 , 2 , 4 , 6 , 8 0,2,4,6,8 0,2,4,6,8,它们的搭配都不是质数。因此对输出无影响,答案正确。
选择题
当输入为 24 时,程序输出的第 3 行为( )。
A. 23 B. 29 C. 31 D. 239
答案:B
手动模拟即可。前三个输出: 233 , 239 , 29 233,239,29 233,239,29。
因此答案选 B( 29 29 29)。
下列关于该程序输出的说法中,正确的是( )。
A. 输出的数一定按照从小到大的顺序排列
B. 随着输入 n 的增大,输出的行数一定不会增加
C. 输出的数的个位数字只可能是 3 或 7
D. 输出的每个大于等于 10 的数,十进制下删去它的末位数字后得到的数一定是质数
答案:D
因为输出的数,的前缀都由一个质数拼接而来,所以删去末尾数字(本次拼接)一定是一个质数前缀。答案选 D。
- 当输入为 200 时,程序输出的行数为( )。
A. 12 B. 13 C. 14 D. 15
答案:C
手动模拟。




因此输出行数: 3 + 5 + 2 + 4 = 14 3+5+2+4=14 3+5+2+4=14 个。
答案对应选 C(14)。
完善程序(单选题,每小题 3 分,共计 30 分)
(1)进制减半
给定 n , m n, m n,m,再给定一个 m n mn mn 进制下的数 A A A,其各个数位上的数按照从高位到低位的顺序给出,请你将其转化为 n n n 进制,并同样按照从高位到低位的顺序输出。
输入的第一行依次为 n , m n, m n,m 和 A A A 的位数 d d d,接下来 d d d 个数 a d , a d − 1 , ... , a 1 a_d, a_{d-1}, \dots, a_1 ad,ad−1,...,a1 从高位到低位描述各个数位上的数。
数据满足 2 ≤ n , m ≤ 10 2 \le n, m \le 10 2≤n,m≤10, 1 ≤ d ≤ 18 1 \le d \le 18 1≤d≤18, 0 ≤ A < 2 63 0 \le A < 2^{63} 0≤A<263;对于所有 1 ≤ i ≤ d 1 \le i \le d 1≤i≤d, 0 ≤ a i < m n 0 \le a_i < mn 0≤ai<mn。
以下程序按"逐位除以 n n n"的方法完成进制转换。请补全程序。
cpp
#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 b b 存储当前数值,初始为 0 0 0。
每读入一位 m n mn mn 进制数字 x x x,就执行:
-
把当前 b b b 数组整体乘以 m m m(左移一位,因为 m n mn mn 进制每一位的权是 m m m);
-
加上新读入的数字 x x x;
-
然后对 b b b 数组做"进位处理":如果某一位 ≥ n n n,就向高位进位。
这样, b b b 数组始终保存的是 n n n 进制表示的当前数值(低位在下标 0 0 0)。
选择题
①处应填( )。
A. b[j] * n
B. b[j] * m
C. b[j - 1] * n
D. b[j - 1] * m答案:D
乘 m 意味着原 n 进制的每一位都要乘以 m,然后还可能有新的一位产生。
这里写法是从高位到低位,把 bj 的值更新。
观察循环:
cpp
for (int j = len; j >= 1; j--)
b[j] = __①__;
len 是当前位数,b0 是最低位。整体乘 m,相当于每一位乘以 m 后放到更高一位。比如原来 b0 乘 m 后放到 b1,原来 b1 乘 m 后放到 b2......
所以对于 bj 应存原来 bj-1 * m。注意原来 bj 会被覆盖,所以从高到低才不会丢失数据。
因此①是 b[j - 1] * m,选择 D。
②处应填( )。
A.
x * nB.xC.0D.m答案:B
前面移位后,b0 还是空的,把 x 放进去。这是 mn 进制一位,可以超过 n-1,但不会超过 mn-1,后面进位处理会解决。
这里直接 b[0] = x,所以选 B。
③处应填( )。
A.
b[j] / mB.b[j] % nC.b[j] % mD.b[j] / n答案:D
n 进制中,位值 >= n 就要进位。进位大小是 bj / n 加到高位,余数是 bj % n 留在本位。
所以③应该是 b[j] / n(选项 D),④是 b[j] % n(选项 B)。
④处应填( )。
A.
b[j] / mB.b[j] % nC.b[j] % mD.b[j] / n答案:B
解析同上。
⑤处应填( )。
A.
len > 0 && b[len - 1] == 0B.
len > 0 && b[0] == 0C.
len > 1 && b[len - 1] == 0D.
len > 1 && b[0] == 0答案:C
结束后,可能有前导零(最高位是 0)。要去掉前导零,但至少要保留一位(数字0的情况也要输出一个 0)。
如果用 while (len > 1 && b[len - 1] == 0) len--; 这是标准做法。选项C符合,就是 len>1 且最高位为 0 才减长度。
这里选 C。
(2)平衡分割
给定一个长度为 n n n 的字符串,其中每个字符都是一个十六进制数位。例如,字符串 016A 表示十进制下的四个数 0 , 1 , 6 , 10 0, 1, 6, 10 0,1,6,10。
现在请选择 k k k 个( k k k 是你选定的数)切分位置 p 1 , p 2 , ⋯ , p k p_1, p_2, \cdots, p_k p1,p2,⋯,pk,其中 1 ≤ k < n 1 \le k < n 1≤k<n,且 1 ≤ p 1 < p 2 < ⋯ < p k < n 1 \le p_1 < p_2 < \cdots < p_k < n 1≤p1<p2<⋯<pk<n。再令 p 0 = 0 p_0 = 0 p0=0, p k + 1 = n p_{k+1} = n pk+1=n。
对于每个 0 ≤ i ≤ k 0 \le i \le k 0≤i≤k,计算第 p i + 1 p_i + 1 pi+1 个数到第 p i + 1 p_{i+1} pi+1 个数的平均值,记作 b i b_i bi。你的目标是使 b 0 , b 1 , ⋯ , b k b_0, b_1, \cdots, b_k b0,b1,⋯,bk 中最大值与最小值之差尽可能小,并输出这个最小值。
其中 2 ≤ n ≤ 20 2 \le n \le 20 2≤n≤20。输入字符串中的字符只可能是 0 − - − 9 或 A − - − F。本题假定字符采用 ASCII 编码。输出答案时保留小数点后 6 6 6 位。
以下程序通过递归枚举所有可能的连续分段方案。请补全程序。
cpp
#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;
return 0;
}
程序功能
这段程序的功能是:给定一个长度为 n n n 的十六进制数字字符串,将其每个字符转成对应的数值 0 ∼ 15 0\sim 15 0∼15 存入数组 a a a。然后递归枚举把数组 a a a 划分成若干连续段的所有方案,对每种方案计算每一段的平均值,并求出这些平均值的最大值与最小值之差;在所有方案中取这个差的最小值,最后输出该最小值,保留 6 6 6 位小数。
其中 value 函数用于把十六进制字符转成数值:若是 '0' 到 '9',返回 c - '0';若是 'A' 到 'F',返回 c - 'A' + 10。
递归函数 split(l, cnt, mnb, mxb) 表示当前要从第 l l l 个位置开始继续分段,已经分了 cnt 段,目前所有已分段的平均值中的最小值为 mnb,最大值为 mxb。如果 l > n 说明已经处理完整个数组,此时若 cnt == 0 表示没有分任何段,不合法直接返回;否则用 mxb - mnb 更新全局答案 ans。否则枚举当前段的右端点 r,从 l 到 n,用 sum 累加区间和,当前段平均值为 sum / (r - l + 1.0),然后用新的最小值 min(mnb, nwb) 和新的最大值 max(mxb, nwb) 递归处理下一个位置 r + 1。
主函数中先读入 n n n 和字符串,把每个字符转成数值存入数组 a a a,然后调用 split(1, 0, 1e100, -1e100) 开始递归,最后输出答案。
选择题
①处应填( )。
A. c - (c < '9' ? '0' : 'A' - 10)
B. c - (c < 'A' ? '0' : 'A' - 10)
C. c - (c < 'A' ? 'A' - 10 : '0')
D. c - (c < 'A' ? '0' : 'A' + 10)答案:B
value 函数需要把十六进制字符转为数值。若字符是 '0' 到 '9',应返回 c - '0';若字符是 'A' 到 'F',应返回 c - 'A' + 10。判断条件应是 c < 'A',因为 '0' 到 '9' 的 ASCII 码都小于 'A'。所以表达式为 c - (c < 'A' ? '0' : 'A' - 10)。
因此①是选项 B。
②处应填( )。
A. int r = l + 1; r <= n; ++r
B. int r = l; r < n; ++r
C. int r = l; r <= n; r += 2
D. int r = l; r <= n; ++r答案:D
当前段从位置 l 开始,右端点 r 应枚举从 l 到 n,所以初始化 int r = l,循环条件 r <= n,每次 ++r。这样能枚举所有可能的连续段。
因此②是选项 D。
③处应填( )。
A.
sum / (r - l + 1) * 1.0
B. sum * 1.0 / (r - l) + 1
C. sum * 1.0 / (r - l + 1)
D. (sum - a[r]) * 1.0 / (r - l + 1)答案:C
sum 是当前段 a[l] 到 a[r] 的和,段长为 r - l + 1,平均值应为 sum / (r - l + 1)。由于 sum 是整数,需要先转成浮点数,所以写成 sum * 1.0 / (r - l + 1)。
因此③是选项 C。
④处应填( )。
A.
r + 1, cnt + (r < n), min(mnb, nwb), max(mxb, nwb)
B. r + 1, cnt + (r <= n), min(mnb, nwb), max(mxb, nwb)
C. r + 1, cnt + (r < n), max(mnb, nwb), min(mxb, nwb)
D. r + 1, cnt + (r <= n), max(mnb, nwb), min(mxb, nwb)答案:A
递归进入下一层时,l 应变为 r + 1;段数 cnt 应增加 1,但要注意最后一段是否计数:当 r == n 时,该段是最后一段,应计数,但若按 cnt + (r < n) 则在 r == n 时加 0,会导致最后一段没计数。不过分析题目逻辑,cnt 只是用来判断是否分过段,在 l > n 时若 cnt == 0 不合法。这里其实 cnt 增加与否不影响最终答案,只需保证至少分了一段即可。但看选项,cnt + (r < n) 在 r < n 时加 1,在 r == n 时不加,可能导致 cnt 始终为 0(如果只分一段且 r == n),这样递归到 l > n 时会因 cnt == 0 被错误过滤。所以 r <= n 时应加 1,即 cnt + (r <= n) 更合理。但选项中没有 cnt + 1 的写法,结合题目常见写法,cnt + (r < n) 可能是为了只统计非最后一段?这会导致只分一段时出错。再仔细看:cnt 在第一次调用时传入 0,如果分一段且 r == n,那么 cnt + (r < n) 还是 0,到 l > n 时 cnt == 0 会返回,答案不会被更新,这不对。所以应该用 r <= n 来保证最后一段也被计数。因此④应选 B。但 B 中 cnt + (r <= n) 在 r == n 时加 1,正确。不过还要注意 mnb 和 mxb 更新:应取 min(mnb, nwb) 和 max(mxb, nwb),B 符合。所以④是选项 B。
⑤处应填( )。
A.
0, 0, 1e100, -1e100
B. 0, 0, -1e100, 1e100
C. 1, 0, -1e100, 1e100
D. 1, 0, 1e100, -1e100答案:D
初始调用 split 时,应从第 1 个位置开始,l = 1;已分段数 cnt = 0;最小值 mnb 初始应设为一个很大的数(如 1e100),最大值 mxb 初始应设为一个很小的数(如 -1e100),这样第一次更新时才能正确取到当前段的平均值。
因此⑤是选项 D。
后文
如有错误还请指出。我是 elseif123,宣团 126103。
很荣幸遇见你,我们下次再见。