CSP-J 2026 初赛试题解析(第三部分:完善程序题(第一题))精讲



第一题:进制减半------数字魔法转换器

一、故事背景:数字王国的翻译任务

想象一下,我们生活在一个十进制王国。

平常使用的数字是:

复制代码
0、1、2、3、4、5、6、7、8、9

但是,数字王国里还有其他进制的居民。

例如:

  • 二进制:只有0和1。

  • 八进制:使用0~7。

  • 十进制:使用0~9。

  • 十六进制:使用0~9和A~F。


现在,国王给了我们一个任务:

把一个以 m * n 为进制的数字,转换成以 n为进制的数字。

原题将这个转换称为"进制减半",并要求按照从高位到低位的顺序输入各个数位。

举个例子

假设:

复制代码
n = 2
m = 2

那么原来的进制就是:

复制代码
m*n = 2*2 = 4

也就是说,我们要把一个四进制数转换成二进制数。

例如四进制数:

复制代码
123

它表示:

而27的二进制表示是:

复制代码
11011

所以:

复制代码
四进制 123 = 二进制 11011

这就是我们要完成的魔法。


二、为什么不能直接使用普通整数?

原题给出的数字最多有18位,而且可能超过普通整数类型的表示范围。

所以程序没有直接把整个数字存进一个 intlong long 变量,而是采用了数组。

程序的核心数组是:

复制代码
long long b[N];

我们可以把它想象成一排数字储物柜:

复制代码
b[0]  b[1]  b[2]  b[3]  b[4] ...

每个格子保存一位数字。

注意:程序把最低位放在 b[0],而不是最高位。

例如:

复制代码
数字:123

存入数组后:

数组下标 0 1 2
数组内容 3 2 1
对应数位 个位 十位 百位

这样设计有什么好处?

因为进制转换的过程中,需要反复进行除法和取余,而从最低位开始处理非常方便。


三、核心魔法:为什么要不断除以n?

我们先不看代码,理解数学原理。

假设有一个十进制数:

复制代码
27

想把它转换成二进制。

不断除以2:

当前数字 除以2的商 余数
27 13 1
13 6 1
6 3 0
3 1 1
1 0 1

把余数从下往上读:

复制代码
11011

这就是二进制结果。


因此,进制转换的基本思想是:

  1. 不断除以目标进制。

  2. 记录每次除法的余数。

  3. 当商变成0时停止。

  4. 把余数倒过来读。

但是原题的数字可能非常大,不能直接使用普通整数进行除法。

所以程序采用一种更巧妙的方法:

把原来的每一位数字保存到数组中,再模拟"整个数除以n"的过程。


四、程序整体结构

原题程序如下,空缺处用①~⑤表示:

复制代码
#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;
}

我们先抓住整个程序的主线:

读入一位原数 → 更新数组 → 对整个数组进行除法处理 → 去掉前导零 → 输出结果。

下面逐个填空。


五、第34题:①处应该填什么?

原题选项:

A. b[j] * n

B. b[j] * m

C. b[j - 1] * n

D. b[j - 1] * m

1. 先看这段代码

复制代码
for (int j = len; j >= 1; j--)
    b[j] = ①;

它的作用是什么?

每次读入一个新的数位后,程序需要把原来保存的数字整体向高位移动一格,再把新读入的数位放到最低位。

例如原来数组保存:

复制代码
b[2] b[1] b[0]
 1    2    3

现在读入新数位4。

我们需要得到:

复制代码
b[3] b[2] b[1] b[0]
 1    2    3    4

注意数组实际是倒着保存的,所以原来的每个数位都要向更大的下标移动一格。


2. 应该怎样移动?

原来:

复制代码
b[2] = 1;
b[1] = 2;
b[0] = 3;

移动后:

复制代码
b[3] = b[2];
b[2] = b[1];
b[1] = b[0];

因此,正确的移动方式是:

复制代码
b[j] = b[j - 1];

答案

第34题:C。


记忆口诀

数组整体向高位移动:

复制代码
b[j] = b[j - 1];

就是把前一个格子的内容搬到后一个格子里。


六、第35题:②处应该填什么?

原题选项:

A. x * n

B. x

C. 0

D. m


1. 看看新读入的数字放在哪里

代码:

复制代码
b[0] = ②;

我们刚才已经知道:

  • 原来的数字整体向高位移动。

  • 新读入的数位应该放在最低位,也就是 b[0]

假设刚刚读入:

复制代码
x = 7

那么最低位就应该保存7。

因此:

复制代码
b[0] = x;

答案

第35题:B。


记忆口诀

新数字放最低位:

复制代码
b[0] = x;

七、第36题:③处应该填什么?

原题选项:

A. b[j] / m

B. b[j] % n

C. b[j] % m

D. b[j] / n


1. 先理解数组中的进位

我们要把一个数转换成n进制。

在n进制中,每一位只能是:

复制代码
0、1、2、......、n-1

如果某一位达到n或超过n,就必须向高位进位。

例如,在二进制中:

复制代码
2 = 10(二进制)

所以:

复制代码
当前位留下0
向高位进1

对于一般的n进制:

复制代码
当前位的数值 = 原数值 % n
进位 = 原数值 / n

2. 看看原题代码

复制代码
if (b[j] >= n) {
    b[j + 1] += ③;
    b[j] = ④;
}

其中:

复制代码
b[j + 1] += ③;

是把进位加到更高一位。

因此,③应该计算:

当前数位能够向高位贡献多少个n。

也就是:

复制代码
b[j] / n

答案

第36题:D。


八、第37题:④处应该填什么?

原题选项:

A. b[j] / m

B. b[j] % n

C. b[j] % m

D. b[j] / n


1. 当前位需要留下什么?

假设我们正在处理一个二进制数位。

当前格子里有:

复制代码
b[j] = 5

二进制中:

复制代码
5 = 101(二进制)

所以:

复制代码
当前位留下1
向高位进2

一般情况下:

复制代码
当前位留下 b[j] % n

进位则是:

复制代码
b[j] / n

因此:

复制代码
b[j] = b[j] % n;

答案

第37题:B。


记忆口诀

进制转换中的除法与取余:

复制代码
进位 = b[j] / n;
当前位 = b[j] % n;

这与十进制竖式加法中的"满十进一"是同一个思想,只不过这里变成了"满n进一"。


九、第38题:⑤处应该填什么?

原题选项:

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


1. 为什么需要删除前导零?

例如,某个结果在数组中保存为:

复制代码
b[3] = 0
b[2] = 0
b[1] = 1
b[0] = 1

从高位到低位输出:

复制代码
0011

但是我们通常希望输出:

复制代码
11

因此,程序需要把最高位多余的0去掉。


2. 最高位在哪里?

由于数组是倒着保存的:

复制代码
b[0]:最低位
b[1]:次低位
b[2]:更高位
......

所以当前最高位是:

复制代码
b[len - 1]

如果它等于0,就可以把长度减1:

复制代码
len--;

3. 为什么必须保证 len > 1

假设转换结果是0。

如果不限制长度,程序可能不断删除最高位,最后长度变成0,导致输出循环无法正确输出一个0。

因此必须保留至少一位。

正确条件是:

复制代码
len > 1 && b[len - 1] == 0

答案

第38题:C。


十、第一题完整答案与代码

答案汇总

题号 空格 正确选项 填入内容
34 D b[j - 1] * m
35 B x
36 D b[j] / n
37 B b[j] % n
38 C len > 1 && b[len - 1] == 0

填空后的完整程序

复制代码
#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[j - 1] * m;

        b[0] = x;
        len++;

        for (int j = 0; j < len; j++)
            if (b[j] >= n) {
                b[j + 1] += b[j] / n;
                b[j] = b[j] % n;

                if (j + 1 == len) len++;
            }
    }

    while (len > 1 && b[len - 1] == 0)
        len--;

    for (int i = len - 1; i >= 0; i--)
        std::cout << b[i] << ' ';

    return 0;
}

第一题通关口诀

先整体乘m,再加新数位;
满n就进位,余数留当前;
最后去前导零,从高位向低位输出。


相关推荐
haluhalu.2 小时前
初识 Protobuf:微服务跨语言通信的一份契约
java·c语言·开发语言·c++·python
longxuan012 小时前
倍增算法详解
c++·st表·倍增算法
-森屿安年-2 小时前
返回最大数
c++·贪心算法
0+1112 小时前
算法 --二分查找
c++·算法·leetcode
拂拉氏2 小时前
【项目实现】 C++ vector实现(空间配置器+异常处理)
c++·vector·异常·空间配置器
qq_199886872 小时前
第8板块·第3节:设备间拷贝与点对点(P2P)传输
c++·人工智能·gpu算力·cuda
爱奥尼欧3 小时前
【C++】map、set 的底层是什么?红黑树五条性质、插入旋转到模拟实现
开发语言·数据结构·c++
汉克老师3 小时前
CSP-J 2026 初赛试题解析(第三部分:完善程序题(第二题))精讲
c++·csp-j·小学生·学c++编程
kukubuzai3 小时前
双指针系列二--末尾篇(3道题)
c++·算法·leetcode