【2026 CSP-J】【逐题精讲】超详细(15道选择,3道阅读,2道完善)

前言

本文中的题干来自于 洛谷,祝大家取得理想成绩!文中的选项顺序与真实考试会有不同,但内容相同。

如有疏漏,还望指出。如果你喜欢,请留下宝贵的赞,谢谢。

文章将模拟考试思路,在阅读解法的同时,希望读者可以学习答题的方法,并且有所收获。

正文

选择题(共 15 题,每题 2 分,共计 30 分)

第一题

下列 C++ 数据类型中,能够精确存储 10 18 + 1 10^{18}+1 1018+1 这个整数的是( )。

A. float B. long long C. double D. 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. 1364 B. 1635 C. 1405 D. 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

cpp 复制代码
int 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 的值是( )。

cpp 复制代码
int 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,13 B. 8,13 C. 14,7 D. 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. (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。

  1. 将第 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。所以答案错误。

  1. 假设输入为非负整数,则程序输出的第一个数一定不小于第二个数。( )

答案:✔

根据我们前面的分析,说法一定正确。

选择题
  1. 将第 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(陷入死循环)。

  1. 当输入为 6 时,输出为( )。
    A. 3 3 B. 4 2 C. 4 3 D. 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).

  1. 若输入 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。

这个前导零是为了防止最高位相加后的进位问题。

判断题
  1. 当输入为 123 456 时,程序输出为 0579。( )

答案:✔

123 + 456 = 579 123+456=579 123+456=579,且最高位( 1 1 1 和 4 4 4)没有进位,所以含有前导零,为 0579 0579 0579,正确。

  1. 假设输入的两个数均不含前导零,则输出的结果也一定不会含有前导零。( )

答案:❌

根据我们的分析,前导零仅与最高位的进位有关,与输入的数无关。前一题就是一个反例。因此该说法错误。

  1. 将第 21 行改为 c[i] = a[i] + b[i]; 后,程序输出的结果一定比原来的结果小。( )

答案:❌

修改后的含义为:仍然做加法,但是不加进位。

当计算时没有产生任何进位时,结果仍然等于原来的值。如输入 12 34,输出仍为 046。因此答案错误。

应改为"程序输出的结果小于等于原来的结果"。

  1. 假设输入的两个数均为 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 开始尝试。

选择题
  1. 当输入为 10 时,程序的输出共有 10 行。( )

答案:❌

可以手动模拟,以下展示 DeepSeek 的模拟过程:

因此输出共 2 + 2 + 2 + 3 = 9 2+2+2+3=9 2+2+2+3=9 行,不是 10 10 10 行,答案错误。

  1. 若输入的 n n n 不大于 5,则程序的输出中一定包含 5。( )
    答案:✔

5 是素数,输出中一定包含,因此正确。

  1. 若输入的 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,它们的搭配都不是质数。因此对输出无影响,答案正确。

选择题
  1. 当输入为 24 时,程序输出的第 3 行为( )。

    A. 23 B. 29 C. 31 D. 239

答案:B

手动模拟即可。前三个输出: 233 , 239 , 29 233,239,29 233,239,29。

因此答案选 B( 29 29 29)。

  1. 下列关于该程序输出的说法中,正确的是( )。

    A. 输出的数一定按照从小到大的顺序排列

    B. 随着输入 n 的增大,输出的行数一定不会增加

    C. 输出的数的个位数字只可能是 3 或 7

    D. 输出的每个大于等于 10 的数,十进制下删去它的末位数字后得到的数一定是质数

答案:D

因为输出的数,的前缀都由一个质数拼接而来,所以删去末尾数字(本次拼接)一定是一个质数前缀。答案选 D。

  1. 当输入为 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,就执行:

  1. 把当前 b b b 数组整体乘以 m m m(左移一位,因为 m n mn mn 进制每一位的权是 m m m);

  2. 加上新读入的数字 x x x;

  3. 然后对 b b b 数组做"进位处理":如果某一位 ≥ n n n,就向高位进位。

这样, b b b 数组始终保存的是 n n n 进制表示的当前数值(低位在下标 0 0 0)。

选择题
  1. ①处应填( )。

    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。

  1. ②处应填( )。

    A. x * n B. x C. 0 D. m

答案:B

前面移位后,b0 还是空的,把 x 放进去。这是 mn 进制一位,可以超过 n-1,但不会超过 mn-1,后面进位处理会解决。

这里直接 b[0] = x,所以选 B。

  1. ③处应填( )。

    A. b[j] / m B. b[j] % n C. b[j] % m D. b[j] / n

答案:D

n 进制中,位值 >= n 就要进位。进位大小是 bj / n 加到高位,余数是 bj % n 留在本位。

所以③应该是 b[j] / n(选项 D),④是 b[j] % n(选项 B)。

  1. ④处应填( )。

    A. b[j] / m B. b[j] % n C. b[j] % m D. b[j] / n

答案:B

解析同上。

  1. ⑤处应填( )。

    A. len > 0 && b[len - 1] == 0

    B. len > 0 && b[0] == 0

    C. len > 1 && b[len - 1] == 0

    D. 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) 开始递归,最后输出答案。

选择题
  1. ①处应填( )。

    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。

  1. ②处应填( )。

    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。

  1. ③处应填( )。

    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。

  1. ④处应填( )。

    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。

  1. ⑤处应填( )。

    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。

很荣幸遇见你,我们下次再见。

相关推荐
wuyk5551 小时前
Python 网络爬虫入门到实战 第 04 章:请求头、UA 伪装、超时、异常处理、基础反爬绕过
开发语言·爬虫·python
HAHAXX81 小时前
电商RPA批量上架通用方案:一套流程如何同时跑通拼多多、抖店、淘宝和跨境平台
java·运维·rpa
流形填表1 小时前
题库去重实战:基于题干指纹的重复题清理
开发语言·c#
\光辉岁月/2 小时前
3.java运算符
java·开发语言
知识分享小能手2 小时前
C++ 学习教程,从入门到精通,string类和标准模板库 — 完整知识点详解(16)
开发语言·c++·学习
qq_401700412 小时前
Qt 炫酷曲线,图表,2D/3D开源库
开发语言·qt
无忧.芙桃2 小时前
位图妙用:512MB 实现 40 亿整数的存在性查询
c语言·c++·面试·哈希算法
liangshanbo12152 小时前
面试题:线上 JavaScript 报错如何快速定位到源码?
开发语言·javascript·ecmascript
mjhcsp2 小时前
DeepSeek V4.1 Flash 批量处理效能实测
java·前端·数据库