一、单项选择题(共 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++程序代码(点击行号可定位)
- 01#include<iostream>
- 02using namespace std;
- 03int main()
- 04{
- 05 const int SIZE = 10;
- 06 int height[SIZE], num[SIZE], n, ans;
- 07 cin >> n;
- 08 for(int i = 0; i < n; i++){
- 09 cin >> height[i];
- 10 num[i] = 1;
- 11 for(int j = 0; j < i; j++){
- 12 if((height[j] < height[i]) && (num[j] >= num[i]))
- 13 num[i] = num[j] + 1;
- 14 }
- 15 }
- 16 ans = 0;
- 17 for(int i = 0; i < n; i++){
- 18 if(num[i] > ans) ans = num[i];
- 19 }
- 20 cout << ans << endl;
- 21 return 0;
- 22}
▶ 输入样例
10
3 2 5 11 12 7 4 10 15 6
🪜 样例推演 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++程序代码(点击行号可定位)
- 01#include <iostream>
- 02using namespace std;
- 03const int maxn = 10000;
- 04int n;
- 05int a[maxn];
- 06int b[maxn];
- 07int f(int l, int r, int depth) {
- 08 if (l > r)
- 09 return 0;
- 10 int min = maxn, mink;
- 11 for (int i = l; i <= r; ++i) {
- 12 if (min > a[i]) {
- 13 min = a[i];
- 14 mink = i;
- 15 }
- 16 }
- 17 int lres = f(l, mink - 1, depth + 1);
- 18 int rres = f(mink + 1, r, depth + 1);
- 19 return lres + rres + depth * b[mink];
- 20}
- 21int main() {
- 22 cin >> n;
- 23 for (int i = 0; i < n; ++i)
- 24 cin >> a[i];
- 25 for (int i = 0; i < n; ++i)
- 26 cin >> b[i];
- 27 cout << f(0, n - 1, 1) << endl;
- 28 return 0;
- 29}
▶ 输入样例
10
(a 数组,决定树形)
(b 数组,决定权值)
🪜 看清「深度 × 权值」的求和结构
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++程序代码(点击行号可定位)
- 01#include<bits/stdc++.h>
- 02using namespace std;
- 03#define MOD 19260817
- 04#define MAXN 1005
- 05long long A[MAXN][MAXN] = {0}, sum[MAXN][MAXN] = {0};
- 06int n, m, q;
- 07int main(){
- 08 A[1][1] = A[1][0] = 1;
- 09 for(int i = 2; i <= 1000; i++) {
- 10 A[i][0] = 1;
- 11 for(int j = 1; j <= i; j++)
- 12 A[i][j] = (A[i - 1][j] + A[i - 1][j - 1]) % MOD;
- 13 }
- 14 for(int i = 1; i <= 1000; i++)
- 15 for(int j = 1; j <= 1000; j++)
- 16 sum[i][j] = (sum[i - 1][j] + sum[i][j - 1]
- 17 - sum[i - 1][j - 1] + A[i][j] + MOD) % MOD;
- 18 int q;
- 19 cin >> q;
- 20 while(q--){
- 21 int n, m;
- 22 cin >> n >> m;
- 23 cout << sum[n][m] << endl;
- 24 }
- 25 return 0;
- 26}
🪜 搞清两个数组的含义
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++程序代码(带 ①~⑤ 填空标记)
- 01#include<iostream>
- 02using namespace std;
- 03int n, m, i, lbound, ubound, mid, count;
- 04int len[100]; // 绳子长度
- 05int main()
- 06{
- 07 cin >> n;
- 08 count = 0;
- 09 for (i = 0; i < n; i++)
- 10 {
- 11 cin >> len[i];
- 12 ① ;
- 13 }
- 14 cin >> m;
- 15 if ( ② )
- 16 {
- 17 cout << "Failed" << endl;
- 18 return 0;
- 19 }
- 20 lbound = 1;
- 21 ubound = 1000000;
- 22 while ( ③ )
- 23 {
- 24 mid = ④ ;
- 25 count = 0;
- 26 for (i = 0; i < n; i++)
- 27 ⑤ ;
- 28 if (count < m)
- 29 ubound = mid - 1;
- 30 else
- 31 lbound = mid;
- 32 }
- 33 cout << lbound << endl;
- 34 return 0;
- 35}
第①空选项
A. count = count + len[i] * 2
B. count = count + len[i]
C. count = len[i]
D. ubound = len[i]
第②空选项
A. count > m
B. count < m
C. count == m
D. count != m
第③空选项
A. lbound <= ubound
B. lbound < ubound
C. lbound > 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++程序代码(带 ①~⑤ 填空标记)
- 01#include <iostream>
- 02#include <cstring>
- 03using namespace std;
- 04const int SIZE = 25;
- 05bool used[SIZE];
- 06int data[SIZE];
- 07int n, m, i, j, k;
- 08bool flag;
- 09int main()
- 10{
- 11 cin >> n >> m;
- 12 memset(used, false, sizeof(used));
- 13 for(i = 1; i <= m; i++)
- 14 {
- 15 data[i] = i;
- 16 used[i] = true;
- 17 }
- 18 flag = true;
- 19 while(flag)
- 20 {
- 21 for(i = 1; i <= m - 1; i++) cout << data[i] << " ";
- 22 cout << data[m] << endl;
- 23 flag = ① ;
- 24 for(i = m; i >= 1; i--)
- 25 {
- 26 ② ;
- 27 for(j = data[i] + 1; j <= n; j++)
- 28 if(!used[j])
- 29 {
- 30 used[j] = true;
- 31 data[i] = ③ ;
- 32 flag = true;
- 33 break;
- 34 }
- 35 if(flag)
- 36 {
- 37 for(k = i + 1; k <= m; k++)
- 38 for(j = 1; j <= ④ ; j++)
- 39 if(!used[j])
- 40 {
- 41 data[k] = j;
- 42 used[j] = true;
- 43 break;
- 44 }
- 45 ⑤ ;
- 46 }
- 47 }
- 48 }
- 49 return 0;
- 50}
第①空选项
第②空选项
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. 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,与题面样例一致 ✓