🏠

🧪 CSP 初赛模拟考试 · 第四套模拟

选择题 15 题 + 阅读程序 3 段 + 完善程序 2 段 · 含 LIS、笛卡尔树、组合数前缀和、二分答案与全排列枚举

📚 三大模块🎯 选择 30 分📖 阅读 + 完善 70 分⏱ 建议 120 分钟
← 返回主页
正在载入题库…

一、单项选择题(共 15 题,每题 2 分,共 30 分)

1. 易 Linux 下可执行文件的默认扩展名为( )。
A. exe B. com C. dll D. 以上都不是
✅ 正确答案:D
解析:Linux 不依赖扩展名判断文件类型,可执行文件靠权限位 x 决定,通常没有扩展名。exe、dll 是 Windows 的概念,com 是顶级域名。
2. 中 下面的冒泡排序伪代码中,对 n 个数排序最少需要比较多少次?( )
FLAG ← n;while FLAG > 1 do:k ← FLAG − 1;FLAG ← 1;for j = 1 to k:if L(j) > L(j+1) 则交换且 FLAG ← j
A. n² B. n − 2 C. n − 1 D. n
✅ 正确答案:C
解析:最好情况(已经有序):第一轮 k = n − 1,比较 n − 1 次且不发生交换,FLAG 保持 1,while 立即结束。
3. 易 地址总线的位数决定了 CPU 可直接寻址的内存空间大小,例如地址总线为 16 位,其最大的可寻址空间为 64KB。如果地址总线是 32 位,则理论上最大可寻址的内存空间为( )。
A. 128KB B. 1MB C. 1GB D. 4GB
✅ 正确答案:D
解析:n 位地址总线 → 2ⁿ 个地址,每个地址 1 字节。2³² B = 4 × 2³⁰ B = 4GB。
4. 易 无向完全图是图中每对顶点之间都恰好有一条边的简单图。已知无向完全图 G 有 7 个顶点,则它共有( )条边。
A. 7 B. 21 C. 42 D. 49
✅ 正确答案:B
解析:n 个顶点的完全图边数 = n(n − 1)/2 = 7 × 6 / 2 = 21。
5. 易 下列说法正确的是( )。
A. CPU 的主要任务是执行数据运算和程序控制 B. 存储器具有记忆能力,其中信息任何时候都不会丢失 C. 两个显示器屏幕尺寸相同,则它们的分辨率必定相同 D. 个人用户只能使用 Wifi 的方式连接到 Internet
✅ 正确答案:A
解析:B 错(内存断电即失);C 错(尺寸与分辨率无关);D 错(还可用网线、移动网络等)。A 正确。
6. 难 以 A0 作为起点,对下面的无向图进行深度优先遍历时,遍历顺序不可能是( )。
图中 4 个顶点为 A0、A1、A2、A3,除 A0 与 A3 之间没有边以外,其余每两点之间都有边。
A. A0, A1, A2, A3 B. A0, A1, A3, A2 C. A0, A2, A1, A3 D. A0, A3, A1, A2
✅ 正确答案:D
解析:DFS 每一步只能走到相邻的未访问顶点。图中 A0 与 A3 不相邻,所以访问完 A0 后绝不可能紧接着访问 A3,故 D 不可能。
其余三项都可实现:A(A0→A1→A2→A3)、B(A0→A1→A3→A2)、C(A0→A2→A1→A3)。
7. 中 完全二叉树的顺序存储方案中,根结点存放在数组的 1 号位置,则第 k 号结点的父结点如果存在,应当存放在数组的( )号位置。
A. 2k B. 2k + 1 C. ⌊k/2⌋ D. ⌊(k+1)/2⌋
✅ 正确答案:C
解析:编号为 k 的结点,其左右孩子分别是 2k 与 2k + 1;反过来,父结点编号 = ⌊k / 2⌋(k 为偶数时 k/2,k 为奇数时 (k − 1)/2,统一写成下取整)。
8. 易 下列几个 32 位 IP 地址中,书写错误的是( )。
A. 162.105.135.27 B. 192.168.0.1 C. 256.256.129.1 D. 10.0.0.1
✅ 正确答案:C
解析:点分十进制每段必须是 0 ~ 255 的整数,256 越界,故 C 书写错误。
9. 难 设 X、Y、Z 分别代表三进制下的一位数字,若等式 XY + ZX = XYX 在三进制下成立,那么同样在三进制下,等式 XY × ZX = ( )也成立。
A. YXZ B. ZXY C. XYZ D. XZY
✅ 正确答案:B
解析:看个位:Y + X 的结果个位仍是 X ⇒ Y = 0 且不进位。
看前两位:X + Z = X0(三进制),说明有进位且结果为 X0₃,只能是 X = 1、Z = 2。验证:XY = 10₃ = 3,ZX = 21₃ = 7,3 + 7 = 10 = 101₃ = XYX ✓
那么 XY × ZX = 3 × 7 = 21 = 210₃ = ZXY。
10. 中 有 7 个一模一样的苹果,放到 3 个一样的盘子中,盘子可以为空,一共有( )种放法。
A. 7 B. 8 C. 21 D. 37
✅ 正确答案:B
解析:即把 7 拆成不超过 3 个非负整数(不计顺序):007、016、025、034、115、124、133、223,共 8 种。
11. 中 一段文言文只由「之」「乎」「者」「也」4 个汉字组成,出现次数分别为 700、600、300、400。那么「也」字的哈夫曼编码长度可能是( )。
A. 1 B. 2 C. 3 D. 4
✅ 正确答案:C
解析:哈夫曼编码中,出现次数越多的字符编码越短。按次数从多到少:之(700)、乎(600)、也(400)、者(300)。
构造哈夫曼树后 4 个字符的编码长度依次为 1、2、3、4,「也」排第 3 → 长度 3。
12. 中 某算法的时间满足 T(n) = T(n − 1) + n(n 为正整数)且 T(0) = 1,则该算法的时间复杂度为( )。
A. O(log n) B. O(n log n) C. O(n) D. O(n²)
✅ 正确答案:D
解析:T(n) = T(0) + 1 + 2 + … + n = 1 + n(n + 1)/2 = O(n²)。
13. 中 下图表示一个果园灌溉系统,有 A、B、C、D 四个阀门,每个阀门可以打开或关上,所有管道粗细相同,以下设置阀门的方法中,可以让果树浇上水的是( )。
A. B 打开,其他都关上 B. AB 都打开,CD 都关上 C. A 打开,其他都关上 D. D 打开,其他都关上
✅ 正确答案:A
解析:观察管道连通关系,能让水到达果树的方案有两种:① 打开 B 且关上 A;② 打开 C 和 D。四个选项中只有 A(B 打开,其他都关上)满足第一种方案。
14. 中 10000 以内,与 10000 互质的正整数有( )个。
A. 2000 B. 4000 C. 6000 D. 8000
✅ 正确答案:B
解析:10000 = 2⁴ × 5⁴。被 2 整除 5000 个、被 5 整除 2000 个、同时被二者整除 1000 个 → 不互质 5000 + 2000 − 1000 = 6000 个。
互质 = 10000 − 6000 = 4000 个(φ(10000) = 10000 × ½ × ⅘)。
15. 难 0、1、8 颠倒还是本身,6 颠倒成 9,9 颠倒成 6,其余数字颠倒不成数字。车牌由 5 位数字组成,则最多有多少个车牌倒过来恰好还是原来的车牌?( )
A. 60 B. 125 C. 75 D. 100
✅ 正确答案:C
解析:第 1 位与第 5 位成对确定,可取 {0,1,8,6,9} 共 5 种;第 2 位与第 4 位同理 5 种;第 3 位必须颠倒后等于自己,只能取 {0,1,8} 共 3 种。
总数 = 5 × 5 × 3 = 75。

二、阅读程序(第 16~18 题,含判断题与单选题)

阅读 1:最长上升子序列 LIS(第 16 题)中

num[i] 表示「以第 i 个数结尾的最长上升子序列长度」。对每个 i 扫描它前面的所有 j,若 height[j] < height[i] 就尝试用 num[j] + 1 更新。

C++程序代码(点击行号可定位)
  1. 01#include<iostream>
  2. 02using namespace std;
  3. 03int main()
  4. 04{
  5. 05 const int SIZE = 10;
  6. 06 int height[SIZE], num[SIZE], n, ans;
  7. 07 cin >> n;
  8. 08 for(int i = 0; i < n; i++){
  9. 09 cin >> height[i];
  10. 10 num[i] = 1;
  11. 11 for(int j = 0; j < i; j++){
  12. 12 if((height[j] < height[i]) && (num[j] >= num[i]))
  13. 13 num[i] = num[j] + 1;
  14. 14 }
  15. 15 }
  16. 16 ans = 0;
  17. 17 for(int i = 0; i < n; i++){
  18. 18 if(num[i] > ans) ans = num[i];
  19. 19 }
  20. 20 cout << ans << endl;
  21. 21 return 0;
  22. 22}
▶ 输入样例
10
3 2 5 11 12 7 4 10 15 6
▶ 输出样例
5
🪜 样例推演 3 2 5 11 12 7 4 10 15 6
1 i = 0:num[0] = 1(前面没有元素)
2 i = 1(2):2 前面没有更小的 → num[1] = 1
3 i = 2(5):3 < 5 → num = 2;2 < 5 → num = 2 → 2
4 i = 3(11):可由 5 接上 → 3;i = 4(12):→ 4
5 i = 5(7):前面 3、2、5 都更小 → 3;i = 6(4):→ 3
6 i = 7(10):接在 7 后面 → 4;i = 8(15):接在 12 后面 → 5
7 i = 9(6):→ 3。最大值为 5(如 2 5 7 10 15)
(1) 如果 height 数组中的输入有负数,则程序会出错。
A. 正确 B. 错误
✅ 答案:B(错误)
解析:程序只做大小比较与赋值,负数参与比较完全合法,LIS 的定义也不要求元素为正,不会出错。
(2) 程序输出的 ans 小于或等于 n。
A. 正确 B. 错误
✅ 答案:A(正确)
解析:ans 是「最长上升子序列的长度」,子序列长度不可能超过原序列长度 n。
(3) 将第 12 行的 "num[j] >= num[i]" 改为 "num[j] > num[i]",程序的输出结果不会改变。
A. 正确 B. 错误
✅ 答案:B(错误)
解析:反例:输入 [1, 2]。i = 1 时 num[0] = 1、num[1] = 1,原写法 1 ≥ 1 成立 → num[1] = 2;改成 > 后条件不成立,num[1] 保持 1。输出由 2 变成 1,结果改变。
(4) 将第 18 行的 "num[i] > ans" 改为 "num[i] >= ans",程序的输出结果不会改变。
A. 正确 B. 错误
✅ 答案:A(正确)
解析:相等时执行 ans = num[i] 只是把同样的值再赋一次,最终结果不变。
(5) 若输入为 10 与 10 个 1,则输出结果是( )。
A. 4 B. 3 C. 2 D. 1
✅ 答案:D
解析:所有元素相等,height[j] < height[i] 永远不成立,num[i] 全为 1,输出 1。
(6) 若输入为 10 与 3 2 5 11 12 7 4 10 15 6,则输出结果是( )。
A. 4 B. 5 C. 6 D. 7
✅ 答案:B
解析:LIS 如 2 → 5 → 7 → 10 → 15,长度 5。

阅读 2:笛卡尔树(第 17 题)难

f(l, r, depth):在区间 [l, r] 中找最小值位置 mink 作为根,左右两侧递归成左右子树,返回 Σ depth × b[mink]。这棵树叫笛卡尔树,每个位置的「深度」就是它到最小值的包含层数。

C++程序代码(点击行号可定位)
  1. 01#include <iostream>
  2. 02using namespace std;
  3. 03const int maxn = 10000;
  4. 04int n;
  5. 05int a[maxn];
  6. 06int b[maxn];
  7. 07int f(int l, int r, int depth) {
  8. 08 if (l > r)
  9. 09 return 0;
  10. 10 int min = maxn, mink;
  11. 11 for (int i = l; i <= r; ++i) {
  12. 12 if (min > a[i]) {
  13. 13 min = a[i];
  14. 14 mink = i;
  15. 15 }
  16. 16 }
  17. 17 int lres = f(l, mink - 1, depth + 1);
  18. 18 int rres = f(mink + 1, r, depth + 1);
  19. 19 return lres + rres + depth * b[mink];
  20. 20}
  21. 21int main() {
  22. 22 cin >> n;
  23. 23 for (int i = 0; i < n; ++i)
  24. 24 cin >> a[i];
  25. 25 for (int i = 0; i < n; ++i)
  26. 26 cin >> b[i];
  27. 27 cout << f(0, n - 1, 1) << endl;
  28. 28 return 0;
  29. 29}
▶ 输入样例
10
(a 数组,决定树形)
(b 数组,决定权值)
▶ 输出样例
385 / 580(见下方小题)
🪜 看清「深度 × 权值」的求和结构
1 L11~L16 找区间最小值(第一次出现的最小值)作为根
2 L17~L19 左右区间递归,深度 +1,返回三者之和
3 整体含义:每个位置 i 的 depth 乘上 b[i],再全部相加
4 a 数组只决定「树长什么样」(即每个位置的深度),b 数组决定这一层的贡献
5 想让结果最大:让 b 中较大的值落在更深的位置 → 构造一条链
6 想让结果最小:让树尽量平衡(完全二叉树时各点深度之和最小)
(1) 如果 a 数组有重复的数字,则程序运行时会发生错误。
A. 正确 B. 错误
✅ 答案:B(错误)
解析:查找最小值用的是严格小于(min > a[i]),重复元素时取第一次出现的那个当根,仍然能建出一棵合法的笛卡尔树。即使所有元素都相同,也只会退化成一条只有右孩子的链,不会出错。
(2) 如果 b 数组全为 0,则输出为 0。
A. 正确 B. 错误
✅ 答案:A(正确)
解析:每一层的返回值都是 depth × b[mink],b 全为 0 时每一项都是 0,累加结果自然为 0。
(3) 当 n = 100 时,最坏情况下第 12 行的比较运算执行次数最接近( )。
A. 5000 B. 600 C. 6 D. 100
✅ 答案:A
解析:最坏情况是树退化成一条链(最小值总在最边上),每层比较次数依次 100、99、98、…,总和 ≈ 5050,最接近 5000。
(4) 当 n = 100 时,最好情况下第 12 行的比较运算执行次数最接近( )。
A. 100 B. 6 C. 5000 D. 600
✅ 答案:D
解析:最好情况是树尽量平衡,每层比较次数约 100、50 + 50、…、树高约 7 层(2⁶ < 100 ≤ 2⁷),总次数在 600 ~ 700 之间,最接近 600。
(5) 当 n = 10 且 b[i] = i + 1 时,输出最大为( )。
A. 386 B. 383 C. 384 D. 385
✅ 答案:D
解析:让 b 的大值对应更深的深度:构造一条链(a 单调不降),深度依次为 1 ~ 10,结果 = 10×10 + 9×9 + … + 1×1 = 385。
(6) 当 n = 100 且所有 b[i] = 1 时,输出最小为( )。
A. 582 B. 580 C. 579 D. 581
✅ 答案:B
解析:b 全为 1 时结果就是所有结点深度之和。100 个结点的完全二叉树:各层结点数 1、2、4、8、16、32、37,深度和 = 1×1 + 2×2 + 3×4 + 4×8 + 5×16 + 6×32 + 7×37 = 580。

阅读 3:组合数 + 二维前缀和(第 18 题)中

先递推组合数 A[i][j] = C(i, j)(对 MOD 取模),再求二维前缀和 sum[i][j],每次询问 O(1) 回答「左上角 i × j 区域内所有组合数之和」。

C++程序代码(点击行号可定位)
  1. 01#include<bits/stdc++.h>
  2. 02using namespace std;
  3. 03#define MOD 19260817
  4. 04#define MAXN 1005
  5. 05long long A[MAXN][MAXN] = {0}, sum[MAXN][MAXN] = {0};
  6. 06int n, m, q;
  7. 07int main(){
  8. 08 A[1][1] = A[1][0] = 1;
  9. 09 for(int i = 2; i <= 1000; i++) {
  10. 10 A[i][0] = 1;
  11. 11 for(int j = 1; j <= i; j++)
  12. 12 A[i][j] = (A[i - 1][j] + A[i - 1][j - 1]) % MOD;
  13. 13 }
  14. 14 for(int i = 1; i <= 1000; i++)
  15. 15 for(int j = 1; j <= 1000; j++)
  16. 16 sum[i][j] = (sum[i - 1][j] + sum[i][j - 1]
  17. 17 - sum[i - 1][j - 1] + A[i][j] + MOD) % MOD;
  18. 18 int q;
  19. 19 cin >> q;
  20. 20 while(q--){
  21. 21 int n, m;
  22. 22 cin >> n >> m;
  23. 23 cout << sum[n][m] << endl;
  24. 24 }
  25. 25 return 0;
  26. 26}
▶ 输入样例
1
5 3
▶ 输出样例
50
🪜 搞清两个数组的含义
1 L08~L13 组合数递推:A[i][0] = 1,A[i][j] = A[i−1][j] + A[i−1][j−1] → A[i][j] = C(i, j),j > i 的位置保持 0
2 L14~L17 二维前缀和:sum[i][j] = sum[i−1][j] + sum[i][j−1] − sum[i−1][j−1] + A[i][j]
3 查询 (n, m) 时输出的是「i ≤ n 且 j ≤ m 的所有 C(i, j) 之和」
4 样例 n = 5、m = 3:Σ C(i, j) = 1 + 3 + 7 + 14 + 25 = 50
5 组合数最大值出现在 C(10, 5) = 252
(1) 当 i < j 时,A[i][j] 的值是 0。
A. 正确 B. 错误
✅ 答案:A(正确)
解析:内层循环 for(j = 1; j <= i; j++) 只填 j ≤ i 的位置,j > i 的位置保持初值 0(因为 j 个里选 i 个以上是不可能的)。
(2) 当 i > j 时,A[i][j] 的值相当于从 i 个不同元素中取出 j 个元素的排列数。
A. 正确 B. 错误
✅ 答案:B(错误)
解析:A[i][j] = C(i, j) 是组合数,不是排列数(排列数还要乘 j!)。
(3) sum[i][j] 的值(1 < j < i < 1000)不小于 sum[i − 1][j − 1] 的值。
A. 正确 B. 错误
✅ 答案:A(正确)
解析:二维前缀和关于两个下标单调不减,i、j 都更大时区域包含关系成立,故 sum[i][j] ≥ sum[i−1][j−1]。
(4) 若将第 12 行改为 "A[i][j] = (A[i-1][j] + A[i-1][j-1] + MOD) % MOD;",程序的运行结果不变。
A. 正确 B. 错误
✅ 答案:A(正确)
解析:(x + MOD) % MOD = x % MOD(MOD 的倍数取模后为 0),加一个 MOD 只是避免出现负数,结果完全相同。
(5) A[i][j](1 ≤ i ≤ 10,1 ≤ j ≤ 10)的所有元素中,最大值是( )。
A. 126 B. 276 C. 252 D. 210
✅ 答案:C
解析:组合数在中间取最大:C(10, 5) = 252。
(6) 若输入数据为 1 / 5 3(/ 为换行符),则输出为( )。
A. 10 B. 35 C. 50 D. 24
✅ 答案:C
解析:Σi=1..5 Σj=1..3 C(i, j) = 1 + 3 + 7 + 14 + 25 = 50。

三、完善程序(第 19~20 题)

完善 1:切割绳子(二分答案,第 19 题)中

先判断「每段切 1 cm」的总段数是否够 m 段;再用二分在 [1, 1000000] 中找能切出至少 m 段的最大长度。与 NOIP2017 真题同题。

C++程序代码(带 ①~⑤ 填空标记)
  1. 01#include<iostream>
  2. 02using namespace std;
  3. 03int n, m, i, lbound, ubound, mid, count;
  4. 04int len[100]; // 绳子长度
  5. 05int main()
  6. 06{
  7. 07 cin >> n;
  8. 08 count = 0;
  9. 09 for (i = 0; i < n; i++)
  10. 10 {
  11. 11 cin >> len[i];
  12. 12 ① ;
  13. 13 }
  14. 14 cin >> m;
  15. 15 if ( ② )
  16. 16 {
  17. 17 cout << "Failed" << endl;
  18. 18 return 0;
  19. 19 }
  20. 20 lbound = 1;
  21. 21 ubound = 1000000;
  22. 22 while ( ③ )
  23. 23 {
  24. 24 mid = ④ ;
  25. 25 count = 0;
  26. 26 for (i = 0; i < n; i++)
  27. 27 ⑤ ;
  28. 28 if (count < m)
  29. 29 ubound = mid - 1;
  30. 30 else
  31. 31 lbound = mid;
  32. 32 }
  33. 33 cout << lbound << endl;
  34. 34 return 0;
  35. 35}
第①空选项
A. count = count + len[i] * 2
B. count = count + len[i]
C. count = len[i]
D. ubound = len[i]
第②空选项
A. count &gt; m
B. count &lt; m
C. count == m
D. count != m
第③空选项
A. lbound &lt;= ubound
B. lbound &lt; ubound
C. lbound &gt; ubound
D. lbound != ubound
第④空选项
A. (lbound + ubound) / 2
B. (lbound + ubound + 1) / 2
C. lbound + 1
D. ubound - 1
第⑤空选项
A. count = count + len[i] / mid
B. count = count + len[i] % mid
C. count = len[i] / mid
D. count = count + mid / len[i]
📋 查看各空答案与解析

第①空:B 累加总长度(等价于每段 1 cm 能切多少段),用于判断是否有解。

第②空:B 总长度不够 m 段 → 无解,输出 Failed。

第③空:B 二分继续的条件:区间还有至少两个值。收缩到 lbound == ubound 时结束。

第④空:B 必须向上取整:(l + r + 1) / 2。若用 (l + r) / 2,当 lbound = mid 时区间可能不缩小,从而死循环。

第⑤空:A 按当前长度统计段数:第 i 根可切 len[i] / mid 段,累加起来与 m 比较。

🪜 二分答案的「判定 + 收缩」套路
1 判定函数:给定段长 mid,能切的段数 = Σ ⌊len[i] / mid⌋
2 段数 ≥ m → 说明还能更长,把左端点抬到 mid(lbound = mid)
3 段数 < m → 太长了,把右端点降到 mid − 1
4 因为要找最大可行值,mid 取 (l + r + 1) / 2(向上取整)避免死循环
5 样例:len = [8, 6, 10]、m = 5 → 答案为 4

完善 2:按字典序输出所有排列(第 20 题)难

从最小组合 1 2 … m 出发,每轮:从末尾往前找第一个还能增大的位置 i,把它换成更大的未使用数字,再把 i 之后的位置用剩下最小的数字填满。

C++程序代码(带 ①~⑤ 填空标记)
  1. 01#include <iostream>
  2. 02#include <cstring>
  3. 03using namespace std;
  4. 04const int SIZE = 25;
  5. 05bool used[SIZE];
  6. 06int data[SIZE];
  7. 07int n, m, i, j, k;
  8. 08bool flag;
  9. 09int main()
  10. 10{
  11. 11 cin >> n >> m;
  12. 12 memset(used, false, sizeof(used));
  13. 13 for(i = 1; i <= m; i++)
  14. 14 {
  15. 15 data[i] = i;
  16. 16 used[i] = true;
  17. 17 }
  18. 18 flag = true;
  19. 19 while(flag)
  20. 20 {
  21. 21 for(i = 1; i <= m - 1; i++) cout << data[i] << " ";
  22. 22 cout << data[m] << endl;
  23. 23 flag = ① ;
  24. 24 for(i = m; i >= 1; i--)
  25. 25 {
  26. 26 ② ;
  27. 27 for(j = data[i] + 1; j <= n; j++)
  28. 28 if(!used[j])
  29. 29 {
  30. 30 used[j] = true;
  31. 31 data[i] = ③ ;
  32. 32 flag = true;
  33. 33 break;
  34. 34 }
  35. 35 if(flag)
  36. 36 {
  37. 37 for(k = i + 1; k <= m; k++)
  38. 38 for(j = 1; j <= ④ ; j++)
  39. 39 if(!used[j])
  40. 40 {
  41. 41 data[k] = j;
  42. 42 used[j] = true;
  43. 43 break;
  44. 44 }
  45. 45 ⑤ ;
  46. 46 }
  47. 47 }
  48. 48 }
  49. 49 return 0;
  50. 50}
第①空选项
A. true
B. false
C. n
D. m
第②空选项
A. used[data[i]] = true
B. used[data[i]] = false
C. data[i] = 0
D. used[i] = false
第③空选项
A. i
B. j
C. data[i] + 1
D. n
第④空选项
A. m
B. i
C. n
D. SIZE
第⑤空选项
A. continue
B. break
C. return 0
D. flag = false
📋 查看各空答案与解析

第①空:B 先假设再也找不到下一个组合,把 flag 置 false;若后面真的找到了可行的新组合,再置回 true。

第②空:B 把位置 i 上原来的数字释放掉:used[data[i]] = false,这样后面才能从 data[i] + 1 开始重新挑数字。

第③空:B 填入找到的新数字 j。

第④空:C 在 1 ~ n 中找未使用的最小数填到后面的位置,保证字典序最小。

第⑤空:B 填完后面所有位置就可以退出本轮调整(break 跳出 i 循环,回到输出)。

🪜 以 n = 3、m = 2 为例走一遍
1 初始 data = [1, 2],输出 1 2
2 flag = false;i = 2:释放 2,从 3 开始找 → 找到 3,data = [1, 3],flag = true,后面没有位置要填,break
3 输出 1 3;i = 2:释放 3,j 从 4 开始 > n,找不到;i = 1:释放 1,从 2 开始找 → 找到 2,data[1] = 2,flag = true
4 填 k = 2:从 1 开始找未使用的 → 1,data = [2, 1],break
5 输出 2 1 → 2 3 → 3 1 → 3 2,与题面样例一致 ✓