夜雨聆风学习资料网

ARTICLE · 1085878

GESP 2026年9月 C++ 七级真题,答案与知识点解析

GESP 2026年9月 C++ 七级真题,答案与知识点解析

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

第 1 题

下列 C++ 代码的输出结果是(  )。

C++

#include <iostream>

using namespace std;

int main() {

int a = 5, b = 3;

cout << (a & b) + (a | b) << endl;

return 0;

}

A. 6

B. 7

C. 8

D. 9

答案:C

知识点解析

本题考查按位与、按位或运算。将 5 写成二进制 101,3 写成 011:按位与得 001(即 1),按位或得 111(即 7),输出为 1 + 7 = 8。这里用到了恒等式 a + b = (a & b) + (a | b):与运算是“公共部分只算一次”,或运算是“合并部分都算一次”,两者相加恰好等于 a + b = 8。选项 B(7)只是 a|b 的中间结果,选项 A(6)恰为 a^b = 101^011 = 110 的值,均不是最终答案。

第 2 题

使用 cmath 或 math.h 中的数学库函数,下列说法中正确的是(  )。

A. pow(2, 3) 的返回值类型为 int

B. sin(30) 的参数 30 表示 30 度

C. sqrt(4) 的返回值类型为 int

D. log(1) 的返回值为 0.0,且类型为 double

答案:D

知识点解析

本题考查 cmath 数学库函数的参数含义与返回值类型。pow(2, 3) 和 sqrt(4) 的返回值类型都是 double 而非 int,故 A、C 错误;sin、cos 等三角函数的参数是弧度而不是角度,传入 30 表示 30 弧度,B 错误;log 是自然对数,log(1) = ln 1 = 0.0,且所有浮点函数返回值类型均为 double,D 正确。

第 3 题

有 4 个字符,出现次数分别为 1、2、3、4。构造哈夫曼树后,出现次数为 1 的字符的哈夫曼编码长度为(  )。

A. 1

B. 2

C. 3

D. 4

答案:C

知识点解析

本题考查哈夫曼树的构造过程。哈夫曼树每次取出权值(出现次数)最小的两棵树合并:先合并 1 和 2 得 3,再合并 3 和 3 得 6,最后合并 6 和 4 得 10。出现次数为 1 的字符在第一次合并时就与权 2 的字符结合,位于树的最深层,从根到它的路径经过 3 条边,所以编码长度为 3。规律是:越早被合并(权值越小)的叶子越深,编码越长。

第 4 题

从 4 × 5 个点连接成的网格的左上角走到右下角,每次只能向右或向下移动,不同的路径共有(  )条。

A. 20

B. 35

C. 70

D. 126

答案:B

知识点解析

本题考查组合计数的网格路径模型。4 × 5 个点构成的网格,从左上角走到右下角需要向下 3 步、向右 4 步,共 7 步。路径由这 7 步的排列顺序决定,只需在 7 步中选出哪 3 步向下(其余向右),共有 C(7, 3) = 35 条。选项 C 的 70 = C(8, 4)、D 的 126 = C(9, 4) 分别对应更大的点阵,是把步数算多后的结果。

第 5 题

在含有 n 个结点的二叉排序树中查找一个元素,平均时间复杂度和最坏时间复杂度分别为(  )。

A. O(log n)、O(n)

B. O(n)、O(log n)

C. O(log n)、O(log n)

D. O(1)、O(n)

答案:A

知识点解析

本题考查二叉排序树(BST)的查找效率分析。当树形比较平衡(接近完全二叉树)时,每次比较都能排除一半结点,平均查找长度为 O(log n);但当插入顺序不当(例如按有序序列依次插入)时,树会退化成一条链,深度为 n − 1,最坏查找需要 O(n)。注意 BST 不像平衡树那样自带平衡机制,所以最坏情形无法保证 O(log n)。

第 6 题

有 4 堆石子,数量分别为 1、2、3、4。每次可以合并相邻两堆,合并代价为两堆石子数之和。将所有石子合并成一堆的最小总代价为(  )。

A. 17

B. 19

C. 20

D. 23

答案:B

知识点解析

本题考查区间 DP 思想(石子合并问题),只能合并相邻两堆。每合并一次,代价为两堆之和,等价于每堆石子在每次包含它的合并中都被累加一次,因此让“大堆”尽量晚参与合并更优。最优方案:先并 1、2(代价 3),得 3, 3, 4;再并 3、3(代价 6),得 6, 4;最后并 6、4(代价 10),总代价 3 + 6 + 10 = 19。若先合并 3、4(代价 7),总代价为 7 + 3 + 10 = 20(选项 C)。要注意“相邻最小”的贪心只是在本题数据下恰好取得最优解,石子合并的一般情形仍需用区间 DP 求解。

第 7 题

在无权图中,使用 BFS 从起点开始遍历,并在访问由结点 u 扩展的相邻结点 v 时记录 dist[v] = dist[u] + 1,且起点的 dist 为 0,则最终 dist[v] 表示的是(  )。

A. 起点到结点 v 的最少边数

B. 结点 v 的度数

C. 从起点到结点 v 的路径上经过的最大边权

D. 包含结点 v 的连通块大小

答案:A

知识点解析

本题考查 BFS 在无权图最短路中的应用。BFS 借助队列按“层”扩展:先访问距起点 1 条边的结点,再访问 2 条边的结点……因此每个结点第一次被访问(标记 vis)时,对应的 dist[v] 就是起点到 v 的最少边数。选项 B 的度数、D 的连通块大小与遍历顺序无关,选项 C 涉及“最大边权”,而本图是无权图,边权概念不存在。

第 8 题

在二维网格上实现泛洪填充时,为了防止递归层数过深,最适合的非递归实现方式是(  )。

A. 使用哈希表记录每个格子被访问的次数

B. 使用快速排序预处理网格

C. 使用二分查找定位边界

D. 使用队列实现 BFS 或使用显式栈模拟 DFS

答案:D

知识点解析

本题考查搜索的非递归实现。递归 DFS 的“层次”保存在系统调用栈上,网格很大时递归深度可达格子总数,容易栈溢出。改用显式队列(BFS)或显式栈(模拟 DFS),把待访问格子存放在堆上的数据结构中,即可避免递归层数过深的问题。选项 A、B、C 与防止递归过深没有任何关系,属于无关干扰项。

第 9 题

关于哈希表,下列说法正确的是(  )。

A. 只要哈希函数选择合适,就可以完全避免冲突

B. 在链地址法中,查找一个元素的时间复杂度一定为 O(1)

C. 开放定址法发生冲突后,会在表内寻找下一个可用位置

D. 哈希表的查找速度与表中元素个数无关

答案:C

知识点解析

本题考查哈希表的冲突处理方法。开放定址法的特点是:所有元素都存放在表内,发生冲突时按探测序列(线性探测、平方探测等)在表内继续寻找下一个可用位置,C 正确。A 错误:无论哈希函数多好,只要 key 的取值空间大于表长,冲突就不可避免;B 错误:链地址法中同一槽位链表过长时查找退化为 O(链长);D 错误:元素越多装填因子越大,冲突越频繁,查找速度越慢。

第 10 题

下列 C++ 代码的输出结果是(  )。

C++

#include <iostream>

using namespace std;

void inc(int &x) {

x++;

}

int main() {

int a = 3;

inc(a);

cout << a;

return 0;

}

A. 3

B. 4

C. 5

D. 编译错误

答案:B

知识点解析

本题考查引用传参。void inc(int &x) 中的 x 是实参 a 的别名(引用),函数内 x++ 直接修改的就是 main 中的 a,因此调用后 a = 4。如果把参数写成值传递 int x,则函数内修改的是副本,输出仍为 3。选项 D 错误:引用传参是合法的 C++ 语法,不会编译错误。

第 11 题

用动态规划求两个序列 a₁ 和 a₂ 的最长公共子序列长度,若 dp[i][j] 表示 a₁ 前 i 个元素与 a₂ 前 j 个元素的 LCS 长度。当 a₁[i - 1] == a₂[j - 1] 时,正确的状态转移是(  )。

A. dp[i][j] = dp[i - 1][j - 1] + 1

B. dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

C. dp[i][j] = dp[i - 1][j] + 1

D. dp[i][j] = dp[i][j - 1]

答案:A

知识点解析

本题考查最长公共子序列(LCS)的状态转移方程。当两序列末位字符相等(a₁[i−1] == a₂[j−1])时,这对字符可以直接配成 LCS 的最后一个字符,于是 dp[i][j] = dp[i−1][j−1] + 1。选项 B 的 max(dp[i−1][j], dp[i][j−1]) 是字符不相等时的转移;选项 C、D 的转移只消去一个序列的一个字符,没有把相等字符“配对”,会漏掉最优解。

第 12 题

下列代码是一维数组优化 0/1 背包的核心片段,执行后 dp[8] 的输出结果是(  )。

C++

#include <iostream>

#include <algorithm>

using namespace std;

int main() {

int w = 3, v = 5, W = 8;

int dp[9] = {0};

for (int c = W; c >= w; c--)

dp[c] = max(dp[c], dp[c - w] + v);

cout << dp[8] << endl;

return 0;

}

A. 0

B. 1

C. 3

D. 5

答案:D

知识点解析

本题考查 0/1 背包的一维(滚动数组)优化,核心是容量循环必须倒序。程序中只有一个物品,重量 w = 3、价值 v = 5,背包容量 W = 8。c 从 8 递减到 3:当 c = 8 时,dp[8] = max(dp[8], dp[5] + 5) = 5(此时 dp[5] 还是 0,对应“只放这一个物品”)。倒序枚举保证 dp[c − w] 还是上一行(未考虑本物品)的值,物品不会被重复选取;若正序枚举则变成完全背包,同一物品可放多次。

第 13 题

若要求排序后相等元素的相对顺序保持不变,下列排序算法中最不适宜使用的是(  )。

A. 冒泡排序

B. 插入排序

C. 归并排序

D. 快速排序

答案:D

知识点解析

本题考查排序算法的稳定性。稳定排序要求排序后相等元素的相对顺序保持不变:冒泡排序相邻交换时遇到相等元素不交换;插入排序把元素插到相等元素之后;归并排序合并时优先取左半部分元素——三者都是稳定的。快速排序的划分(Partition)过程依赖远距离交换,相等元素可能被交换到彼此之前,因此是不稳定的,最不适宜用于本题要求。

第 14 题

下列代码片段的时间复杂度为(  )。

C++

long long s = 0;

for (int i = 1; i <= n; i++)

for (int j = 1; j <= n; j += i)

s += i + j;

A. O(n)

B. O(n log n)

C. O(n²)

D. O(n√n)

答案:B

知识点解析

本题考查嵌套循环的复杂度分析(调和级数求和)。内层循环 j 从 1 每次 += i,执行次数约为 ⌈n / i⌉,总次数为 n/1 + n/2 + … + n/n = n(1 + 1/2 + … + 1/n)。括号内是调和级数,其和约为 ln n,所以总时间复杂度为 O(n log n)。选项 C 的 O(n²) 是把内层误当作每次都跑满 n 次,选项 D 的 O(n√n) 是另一种常见的错误估计。

第 15 题

已知 int a[6] = {1, 3, 5, 7, 9, 11}; int *p = a + 1;,则表达式 *(p + 3) 的值是(  )。

A. 5

B. 7

C. 9

D. 11

答案:C

知识点解析

本题考查指针算术。p = a + 1 使 p 指向 a[1](值为 3),指针加法按元素个数移动:p + 3 指向 a[1 + 3] = a[4],其值为 9。容易混淆的是 a[3](值为 7,选项 B),那是把 p 当作 a 起点算的;实际上 p 已经偏移了 1 个元素。选项 A(5)和 D(11)分别是 a[2] 和 a[5] 的值。

二、判断题(每题 2 分,共 20 分)

第 1 题

使用 cmath 或 math.h 中的函数,表达式 exp(0) 的结果值为 1.0,且类型为 double。

答案:√

知识点解析

本题考查指数函数 exp 的求值与返回类型。exp(x) 计算 e^x,而 e⁰ = 1,所以 exp(0) 的值为 1.0;cmath 中的浮点函数统一返回 double 类型。该说法正确。

第 2 题

采用开放定址法处理冲突的哈希表中,删除一个元素后可以直接将该位置置空,不会影响后续查找。

答案:×

知识点解析

本题考查开放定址法的删除操作。开放定址法中,后续元素的查找依赖探测序列:若把某位置直接置空,探测遇到空位就会提前判定“元素不存在”,从而截断探测链,导致存放在其后的同义词无法被找到。正确做法是惰性删除——给该位置打上“已删除”标记,查找时跳过但不终止探测。

第 3 题

在哈夫曼树中,出现次数更多的叶子结点,其深度总是更小。

答案:×

知识点解析

本题考查哈夫曼树的性质。哈夫曼树只保证“频率高的叶子深度不超过频率低的叶子”,即深度与频率成反序关系,但不保证严格更小。反例:四个字符频率为 2、2、3、3 时,合并 2+2 = 4、3+3 = 6、4+6 = 10,四个叶子深度都是 2,频率 3 的叶子并不比频率 2 的更浅。

第 4 题

在一个有向图中,所有顶点的入度之和总是等于所有顶点的出度之和。

答案:√

知识点解析

本题考查有向图的度数性质。每条有向边恰好从一个顶点出发(贡献 1 个出度)、进入一个顶点(贡献 1 个入度),因此所有顶点的入度之和与出度之和都等于图的边数 m,二者必然相等。

第 5 题

广度优先搜索通常借助队列实现,深度优先搜索通常借助栈或递归实现。

答案:√

知识点解析

本题考查两种图遍历的实现方式。BFS 要求“先发现的先扩展”,队列的先进先出特性恰好满足;DFS 要求“一条路走到黑再回溯”,递归调用(隐式使用系统栈)或显式栈的后进先出特性恰好满足。该说法正确。

第 6 题

快速排序的平均时间复杂度为 O(n log n),最坏时间复杂度也为 O(n log n)。

答案:×

知识点解析

本题考查快速排序的复杂度。快排的平均时间复杂度确实是 O(n log n),但在最坏情形下(如序列已有序而每次又固定取首元素为基准),每次划分只能切出一个元素,递归深度达 n,退化为 O(n²)。正确说法是:最坏时间复杂度为 O(n²)。

第 7 题

为解决 0/1 背包问题,使用一维数组优化时,内层容量循环应从大到小枚举。

答案:√

知识点解析

本题考查 0/1 背包一维优化的枚举方向。二维转移 dp[i][c] 依赖的是第 i − 1 行的 dp[i−1][c − w],一维化后必须保证更新 dp[c] 时 dp[c − w] 还是“上一行”的旧值。容量从大到小枚举时,dp[c − w] 尚未被本轮更新,正确;若从小到大枚举,同一物品可能被重复选取,就变成完全背包了。

第 8 题

使用邻接表存储图时,遍历某个顶点的所有邻边所需时间与图中顶点数成正比。

答案:×

知识点解析

本题考查邻接表与邻接矩阵的遍历效率。邻接表只为每个顶点存放其实际拥有的邻边,遍历某顶点全部邻边的时间与该顶点的度数成正比,总遍历为 O(n + m)。与顶点数成正比(O(n))的是邻接矩阵:即使某顶点只有一条邻边,也要扫描一整行。

第 9 题

在按层序从 1 开始对结点编号的完全二叉树中,编号为 i(i > 1)的结点的父结点编号为 ⌊i/2⌋。

答案:√

知识点解析

本题考查完全二叉树的编号性质。层序从 1 开始编号时,结点 i 的左孩子为 2i、右孩子为 2i + 1,反推可知其父结点编号为 ⌊i/2⌋(奇数 i 时为 (i−1)/2,偶数 i 时为 i/2)。这一性质正是二叉堆可以直接用数组存储、通过下标运算访问父子结点的基础。

第 10 题

在定义了数组 int arr[10]; 后,表达式 arr 和表达式 &arr[0] 总是等价的。

答案:×

知识点解析

本题考查数组名与指针的区别。虽然两者的数值(地址)相同,但类型不同:arr 是 int[10] 类型(表达式中退化为 int*),而 &arr[0] 是 int*;取 &arr 得到的是 int(*)[10]。实际差异可以体现出来:sizeof(arr) 为 40(整个数组大小),sizeof(&arr[0]) 为 8(一个指针);且 arr + 1 前进 4 字节,而 (&arr) + 1 前进 40 字节。因此说“总是等价”是错误的。

三、编程题(每题 25 分,共 50 分)

编程 1

必经之路

时间限制 1.0 s 内存限制 512.0 MB

题目描述

给定一张有 n 个结点 m 条边的有向图 G,G 中的结点依次以 1, 2, ..., n 编号。第 i 条边(1 ≤ i ≤ m)从结点 uᵢ 指向结点 vᵢ。

G 中任一入度为 0 的结点可以作为合法起点,任一出度为 0 的结点可以作为合法终点。

如果 G 中所有可能的从合法起点到合法终点的路径都会经过结点 u,则称 u 是必经点。注意必经点可以为合法起点或合法终点。

请你求出 G 中所有必经点的编号。

例如,在下图中合法起点有点 1 与点 2,合法终点有点 7 与点 8。

文本

(1) (5)---->(7)

\ ^ \ ^

v / v /

(3) / (6)

^ \ / \

/ v / v

(2)---->(4) (8)

所有合法起点到合法终点的路径为:

- 1 → 3 → 4 → 5 → 7

- 1 → 3 → 4 → 5 → 6 → 7

- 1 → 3 → 4 → 5 → 6 → 8

- 2 → 3 → 4 → 5 → 7

- 2 → 3 → 4 → 5 → 6 → 7

- 2 → 3 → 4 → 5 → 6 → 8

- 2 → 4 → 5 → 7

- 2 → 4 → 5 → 6 → 7

- 2 → 4 → 5 → 6 → 8

因此必经点有两个,编号分别为 4, 5。

输入格式

第一行,两个正整数 n, m,表示有向图 G 中的结点数与边数。

接下来 m 行,每行两个正整数 uᵢ, vᵢ,表示一条从结点 uᵢ 指向结点 vᵢ 的有向边。

保证 G 中至少有一个合法起点,至少有一个合法终点,且至少存在一条从一个合法起点到一个合法终点路径,同时不存在孤立点(即出度和入度都为 0 的点)。

输出格式

第一行,一个整数,表示必经点的数量 k。

如果存在必经点,则第二行从小到大输出 G 中所有必经点的编号。

样例

输入样例 1

8 9

1 3

2 3

3 4

4 5

5 6

6 7

6 8

2 4

5 7

输出样例 1

2

4 5

输入样例 2

8 9

1 3

2 3

3 4

4 5

5 6

6 7

6 8

2 5

4 7

输出样例 2

0

数据范围

对于 40% 的测试点,保证 1 ≤ n ≤ 100,1 ≤ m ≤ 200。

对于所有测试点,保证 1 ≤ n ≤ 1000,1 ≤ m ≤ 2000。保证 G 中至少有一个合法起点,至少有一个合法终点,且至少存在一条从一个合法起点到一个合法终点路径,同时不存在孤立点(即出度和入度都为 0 的点)。

参考程序(答案)

C++

#include <cstdio>

#include <algorithm>

using namespace std;

const int N = 1005;

const int E = 2005;

int n, m;

int h[N], to[E], nx[E], et;

int id[N], od[N];

bool vis[N];

int q[N], ql, qr;

int ans[N], cnt;

bool chk(int ban) {

ql = qr = 0;

for (int i = 1; i <= n; i++) {

vis[i] = (i == ban);

if (!vis[i] && !id[i]) {

q[++qr] = i;

vis[i] = 1;

}

}

while (ql < qr) {

int u = q[++ql];

for (int i = h[u]; i; i = nx[i]) {

int v = to[i];

if (vis[v])

continue;

if (!od[v])

return 0;

q[++qr] = v;

vis[v] = 1;

}

}

return 1;

}

int main() {

scanf("%d%d", &n, &m);

for (int i = 1; i <= m; i++) {

int u, v;

scanf("%d%d", &u, &v);

to[++et] = v;

nx[et] = h[u];

h[u] = et;

od[u]++;

id[v]++;

}

for (int i = 1; i <= n; i++)

if (chk(i))

ans[++cnt] = i;

printf("%d\n", cnt);

for (int i = 1; i <= cnt; i++)

printf("%d%c", ans[i], " \n"[i == cnt]);

return 0;

}

编程 2

括号序列

时间限制 1.0 s 内存限制 512.0 MB

题目描述

对于字符串 S 与 T,如果从 S 中删除任意多个字符可以得到 T,那么 T 是 S 的子序列。换言之,T 是选取 S 中的若干字符按下标顺序连接而成的。两个子序列不同当且仅当所选取的下标不同。

例如 sun 是 sequence 的子序列,因为从 sequence 中删除 eq、e 和 ce 可以得到 sun;sequence 有 2⁸ 个不同的子序列,其中有空字符串,也有三个不同的子序列 e,因为 sequence 的第 2、5、8 个字符都为 e,分别保留这三个字符得到的子序列是不同的。

对于字符串 S,如果 S 满足以下条件那么 S 是合法括号序列:

- S 是空字符串,或者

- S 可由 (、合法括号序列、) 三者连接得到,或者

- S 可由两个合法括号序列连接得到。

例如 ()、()()、(()) 和 (()()) 都是合法括号序列。但是 (()、)( 不是合法括号序列。

给定一个长度为 n 的仅包含 ( 与 ) 的字符串 S。请你求出 S 所有 2ⁿ 个子序列中有多少个合法括号序列。由于答案可能很大,请你输出答案对 10⁹ 取模的结果。

例如,S 为 ))(()( 时共有 3 个子序列是合法括号序列,分别为空字符串与两个不同的子序列 ()。

输入格式

第一行,一个正整数 n,表示字符串 S 的长度。

第二行,长度为 n 的仅包含 ( 与 ) 的字符串 S。

输出格式

输出一行,一个整数,表示 S 的合法括号子序列的数量对 10⁹ 取模的结果。

样例

输入样例 1

6

))(()(

输出样例 1

3

输入样例 2

34

((((((((((((((((()))))))))))))))))

输出样例 2

333606220

数据范围

对于 40% 的测试点,保证 1 ≤ n ≤ 400。

对于所有测试点,保证 1 ≤ n ≤ 2000。

参考程序(答案)

C++

#include <cstdio>

#include <algorithm>

using namespace std;

const int N = 2005;

const int mod = 1e9;

int n;

char s[N];

int f[N][N];

int main() {

scanf("%d", &n);

scanf("%s", s + 1);

f[0][0] = 1;

for (int i = 1; i <= n; i++)

for (int j = 0; j <= n; j++) {

f[i][j] = f[i - 1][j];

if (s[i] == '(' && j)

f[i][j] = (f[i][j] + f[i - 1][j - 1]) % mod;

if (s[i] == ')' && j < n)

f[i][j] = (f[i][j] + f[i - 1][j + 1]) % mod;

}

printf("%d\n", f[n][0]);

return 0;

}

由于工作量较大,若存在错漏欢迎大家评论区指正。祝各位考生顺利通过!觉得有用,欢迎点赞、在看、转发三连。

相关学习资料