1. 先搞清楚五级考什么:考纲定位与命题逻辑
GESP把C++能力从一级到八级拉了一条很明确的成长线,五级刚好卡在最关键的位置上。我带学生备考时经常说一句话:一级到四级是"学会用C++说话",从五级开始是"学会用C++解决问题"。如果你正在准备2025年或2026年的GESP C++五级考试,第一件要做的事不是刷题,而是先把五级到底考什么、为什么这么考想明白。
从官方大纲和历年真题来看,五级的知识点可以归纳成三条线:语言进阶线、数据结构线和算法入门线。语言进阶线包括指针、引用、结构体、函数传参方式等;数据结构线以链表为核心,顺带涉及二叉树的概念性内容;算法线是五级的重头戏,包括递归、深度优先搜索(DFS)、广度优先搜索(BFS)、二分查找、基础的排序算法(冒泡、插入、选择、归并、快排)和高精度运算。这个组合非常讲究,它不是让考生背语法,而是考验"拿到一个没见过的题,你能不能抽象出数据结构、设计出算法流程、最后用C++把它实现出来"。
很多家长和学生容易误判五级的难度,觉得四级已经考了贪心和分治的初步思想,五级应该就是稍微再难一点。实际情况恰恰相反,五级是从"我会写代码"向"我会写算法"跨越的临界点。前四级允许你用笨办法硬算,只要思路对就能拿分;五级开始,题目会刻意设计数据范围,逼你使用递归和搜索这类更系统的算法,否则就算答案正确也会超时。这也是为什么很多孩子四级轻松通过、五级却要考两三次,核心原因就是算法思维还没转换过来。
1.1 五级在GESP八级里的"分水岭"定位
GESP总共八级,后面的六级、七级、八级分别对标的是NOIP提高组、省选和国赛级别的算法内容,比如动态规划优化、网络流、平衡树、计算几何这些。五级处在初赛水平到普及组水平之间,再往上一步就是正式的信息学竞赛赛道。所以五级考察的算法都是"地基型"的:递归是所有动态规划和搜索算法的基础,DFS/BFS是图论算法的起点,二分查找是各种优化技巧的基石,链表则是最基础的数据结构。
我个人的建议是,如果你未来有走信奥路线的打算,五级不仅要"过",还要"学透"。因为六级开始默认你已经掌握了DFS和BFS的写法,不会再花时间讲基础框架,而是直接上剪枝优化、记忆化搜索、双向BFS这类进阶技巧。五级基础不牢,六级听课会非常痛苦,到时候再回头补成本就高了。
1.2 从历年真题反推:五级命题的三种典型套路
翻一翻近几期的五级真题,你会发现命题组的出题套路其实有迹可循。
第一种是"语法包装题"。表面看是复杂的链表操作或结构体嵌套,实际上考的还是指针和内存管理的基本功,只要把Node节点的定义、头插尾插、遍历删除这些烂熟于心,基本能拿大部分分。第二种是"经典算法裸题",比如直接给你一个整数序列让你做二分查找,或者让你用冒泡排序对结构体排序,这种题本质是送分题,但要求代码细节零失误。第三种是"模型转换题",题目会裹上一层实际问题(比如迷宫最短路、八皇后变形、任务分配方案数),需要你把它抽象成图或树的搜索模型,再用DFS/BFS去解。五级能不能得高分,很大程度上取决于第三种题型做得怎么样。
2. 语言进阶:指针、结构体与链表的底层逻辑
2.1 指针到底怎么理解才不"飘"
指针是很多五级考生的第一道坎。我见过不少学生,概念背得头头是道——"指针就是存放变量地址的变量",但一写代码就懵,一会儿忘记加星号,一会儿箭头和点混用。其实指针没有你想的那么玄,你可以把它理解成快递柜上的取件码。快递柜(内存)里的包裹(变量值)放在某个格子里,取件码本身不是包裹,但它记录了包裹存放在哪个格子。指针变量就是那个取件码,它存的是"包裹存放的位置"。
C++里围绕指针有三个核心操作符,&取地址,*解引用,->用于指针访问结构体成员。
int a = 10; // 一个普通的int变量,值存的是10 int* p = &a; // p存的是a的内存地址 *p = 20; // 通过p去修改a,本质上就是找到a所在的内存格子为什么五级要考指针?因为链表这个数据结构必须依赖指针(或者用数组下标模拟指针),没有指针,节点的"指向下一个节点"就无从说起。考指针不是为了让你做指针算术这种花活,而是为了给链表和树打地基。所以备考时不要沉迷于二级指针、函数指针这种偏门内容,把"指针声明、&取地址、*解引用、指针作为函数参数"这四件事弄扎实就够了。
这里有一个很多新手的迷思:指针作为函数参数到底能不能改变实参?答案是能,前提是你传的是地址。
void change(int* p) { *p = 99; // 通过地址修改,实参a的值会被改变 }如果直接传值(void change(int x)),函数体内修改的只是一份拷贝,实参不受影响。这个区别在链表操作里极其重要——你写头插函数时,如果传的是Node*,那么对指针本身重新赋值并不会影响外面的头指针,必须用Node**或者通过返回值带回新的头指针。这个细节每年都有考生踩坑,你可以现在就记住,后面写链表会省很多事。
2.2 结构体与链表:五级最核心的数据结构
结构体本身不难,就是把若干个不同类型的变量打包成一个自定义类型。五级真正难的是用结构体配合指针实现链表。
先看一个最标准的单向链表节点定义:
struct Node { int data; // 数据域 Node* next; // 指针域,指向下一个节点 };链表的常见操作包括建表(头插法、尾插法)、遍历、插入、删除、反转、查找。这里面最容易出错的,是更新指针时的顺序问题。比如说,要在某个节点p后面插入一个新节点s,正确顺序有两个关键步骤:先把s->next指向p->next,再把p->next指向s。必须先用一个临时指针把原来的后继节点"接住",否则一旦更新了p->next,原来的后继节点就找不到了。
删除节点也是同样的道理:要删除p->next指向的节点,先备份需要删除的节点,然后让p->next指向被删除节点的next,最后释放内存。C++里用delete释放,如果忘记释放,程序不会报错但会内存泄漏,考试环境通常不会因为内存泄漏扣分,但养成好习惯对后续做工程有帮助。
实际操作中,不少同学会纠结一个问题:链表节点到底用new动态创建,还是用数组模拟?我的建议是:掌握动态链表(new/delete)理解原理,但考试写题时优先用数组模拟链表。原因有三个:第一,new和delete在频繁创建销毁节点时有一定性能开销;第二,动态链表容易因为野指针或忘记判空而段错误;第三,数组模拟可以用更短的代码实现同样的效果,且访问节点更方便。下面这个就是经典的静态链表写法:
struct Node { int data; int next; // 存的是下一个节点在数组中的下标 } nodes[100005]; int head = -1, idx = 0; // head是头节点下标,idx记录当前已用节点数 // 头插法 void insertHead(int val) { nodes[idx].data = val; nodes[idx].next = head; head = idx++; }数组模拟链表的关键就是"用下标代替指针",核心逻辑完全一致,只是把Node*换成了int下标。刚开始可能觉得别扭,但写几道题就会体会到它的爽快。
2.3 深拷贝与内存管理:一个隐藏的丢分点
结构体作为函数参数时,很多学生习惯用值传递。如果结构体里只有一个int,值传递没问题;但如果结构体里有指针字段(比如链表节点),值传递会产生浅拷贝——拷贝出来的结构体和原结构体共用同一个指针指向的内存区域。
举个例子,假设你写了一个函数接收Node类型的参数,函数内部访问了这个节点指针指向的下一个节点,看上去没问题,实际上你操作的可能是一个悬空的引用。更危险的情况是,函数返回一个局部结构体,局部变量在函数结束后就销毁了,返回出去的指针就成了野指针。这些都是五级笔试和机试里爱埋的坑。
应对策略很简单:涉及链表节点的操作,函数参数一律传指针(Node*),而不是传结构体本身(Node)。如果确实需要把一个结构体完整复制一份,要手动实现深拷贝,为新节点分配新的内存,再逐个字段赋值。
3. 算法启蒙:递归、DFS与BFS的解题框架
3.1 递归的终止条件怎么找:三个固定要素
递归是五级的绝对核心,因为DFS、BFS、二叉树遍历、归并排序、二分查找全部依赖递归思维。很多学生觉得递归难,是因为他们在脑子里试图"逐层展开"整个调用过程,越展开越乱。正确的学习方式是把递归当成三个问题来解:边界条件是什么?递归公式是什么?每层递归返回什么?
拿计算阶乘举例:
int fac(int n) { if (n == 0 || n == 1) return 1; // 边界 return n * fac(n - 1); // 递归公式 }边界条件负责让递归停下来,递归公式负责把大问题拆成小问题,返回值把每层的结果串起来。三个要素缺一不可。写递归时最忌讳的就是只写递归公式不写边界,然后程序爆栈或者无限递归。
五级考递归通常会结合"斐波那契数列"这类经典题目,但会稍微变形,比如从单纯的求解变成计数问题。这里我强烈建议你掌握记忆化搜索的写法,因为它是递归向动态规划过渡的中间形态:
long long memo[10005]; long long fib(int n) { if (n <= 1) return n; if (memo[n] != -1) return memo[n]; return memo[n] = fib(n - 1) + fib(n - 2); }直接递归求解斐波那契的时间复杂度是O(2^n),n稍微大一点就会卡死;加一个memo数组记录已算过的值,时间复杂度直接降到O(n)。这个优化思路五级不考,但掌握它对理解后续的递归剪枝非常有帮助。
3.2 DFS:不撞南墙不回头,撞了南墙就回头
深度优先搜索是五级最核心的算法,没有之一。它的思路可以极其简洁地概括为:沿着一条路走到黑,走不通就回到上一个岔路口换一条路继续走。实现方式用递归最简洁,因为递归函数天然自带"回溯"这个行为——函数返回时,状态会回到调用前的样子。
DFS的标准框架长这样:
void dfs(int step) { if (到达边界) { 记录答案; return; } for (每个可能的选项) { 做选择; // 比如标记visited dfs(step + 1); // 进入下一层 撤销选择; // 回溯,恢复现场 } }"做选择"和"撤销选择"必须成对出现,这是DFS最容易漏掉的地方。很多同学写着写着忘记在递归返回后恢复现场,结果导致下一轮搜索时状态已经被污染,输出结果全是错的。我教学生时经常强调一句话:你不恢复现场,你就是在教程序说谎。
五级DFS的典型题目包括:全排列生成、组合枚举、迷宫路径搜索、n皇后(简化版)、数独填充等。这些题换汤不换药,核心都是上面那个框架,区别只在于"每个选项"是什么、"边界条件"如何定义。
给一个五级考试热身级别的DFS例题——输出1到n的所有排列:
#include <iostream> using namespace std; int n; int ans[15]; bool used[15]; void dfs(int step) { if (step > n) { for (int i = 1; i <= n; i++) cout << ans[i] << " "; cout << endl; return; } for (int i = 1; i <= n; i++) { if (!used[i]) { used[i] = true; ans[step] = i; dfs(step + 1); used[i] = false; // 关键:回溯,把使用标记还原 } } } int main() { cin >> n; dfs(1); return 0; }这个代码背下来,五级的搜索题能解一半。
3.3 BFS:用队列实现层层扩散
广度优先搜索和DFS走的是完全不同的路线。DFS用递归,BFS用队列;DFS适合求"解是否存在、有多少组解",BFS适合求"最少步数、最短路径"。这是两类问题的本质区别,考试看到"最少""最短"字样时,优先考虑BFS。
BFS的框架是维护一个队列,先把初始状态放进队列,然后每次从队头取出一个状态,考察它所有可能的下一状态,把没访问过的状态放进队尾,直到队列为空:
void bfs(int start) { queue<int> q; q.push(start); vis[start] = true; while (!q.empty()) { int now = q.front(); q.pop(); // 处理当前状态 for (每个可能的下一状态 next) { if (!vis[next]) { vis[next] = true; q.push(next); } } } }BFS和DFS的核心区别可以用一张表来对照:
| 对比维度 | DFS | BFS |
|---|---|---|
| 实现方式 | 递归(栈) | 队列 |
| 适合问题 | 解的存在性、方案枚举 | 最短步数、最小步数 |
| 空间消耗 | 取决于递归深度 | 取决于每层的状态数 |
| 代码难度 | 框架简单但调试难 | 需要维护队列和步数数组 |
| 五级常见场景 | 全排列、迷宫可行路径 | 迷宫最短路、最少操作次数 |
BFS里有一个常见坑:没有在状态入队的瞬间标记访问,而是等它出队时才标记。这样同一个状态可能被重复入队多次,轻则多算几步,重则导致死循环。正确做法是"入队即标记",这个细节每年都有不少人栽跟头。
4. 高频拿分块:排序、二分和高精度运算
4.1 排序算法怎么选:从冒泡到归并的性价比分析
五级大纲要求的排序算法有不少,很多考生觉得"反正有sort函数,我为什么要手写排序?"这个想法很危险。GESP机试环境虽然允许使用algorithm头文件里的sort,但五级考试更看重的是你对排序过程的理解——因为排序本身就是递归和分治思想的最佳载体,而这两个是五级考纲的核心。
冒泡排序和选择排序是入门级的O(n^2)算法,适合理解"交换""比较"的基本操作。插入排序在近乎有序的数据上实际表现很好。但五级真正值得花时间吃透的是归并排序和快速排序,因为它们用到了递归和分治,而且归并排序还能顺手解决"逆序对统计"这类经典问题,快速排序则体现了"划分"的思想,后续的快速选择算法会用到。
手写一个归并排序,重点在于merge函数:
void merge(int a[], int l, int mid, int r) { int tmp[r - l + 1]; int i = l, j = mid + 1, k = 0; while (i <= mid && j <= r) { if (a[i] <= a[j]) tmp[k++] = a[i++]; else tmp[k++] = a[j++]; } while (i <= mid) tmp[k++] = a[i++]; while (j <= r) tmp[k++] = a[j++]; for (int t = 0; t < k; t++) a[l + t] = tmp[t]; } void mergeSort(int a[], int l, int r) { if (l >= r) return; int mid = (l + r) / 2; mergeSort(a, l, mid); mergeSort(a, mid + 1, r); merge(a, l, mid, r); }这段代码里最容易写错的地方是最后把临时数组拷回原数组的循环,很多人抄代码时下标偏移写错,结果排序结果完全不对。建议你亲手在纸上模拟一次merge过程,搞清楚l、mid、r和tmp数组下标的关系。另外,归并排序的复杂度是O(n log n),是五级要求的"高效排序"里最好写的一种。
4.2 二分查找的边界陷阱:为什么你的代码会死循环
二分查找看起来简单——在一个有序序列里找目标值,每次把搜索范围减半。但真正写起来,"边界条件"是让无数考生头大的点:while循环里到底是left < right还是left <= right?更新区间时mid要加一还是减一?死循环了怎么办?
整数二分的标准写法有两个模板,闭区间版和半开区间版。我个人推荐你只记死其中一套,考试时不要临场换。下面这套是左闭右闭区间的写法:
int binarySearch(int a[], int n, int target) { int l = 0, r = n - 1; while (l <= r) { int mid = l + (r - l) / 2; // 用这个写法防止溢出 if (a[mid] == target) return mid; else if (a[mid] < target) l = mid + 1; else r = mid - 1; } return -1; // 没找到 }关键点有三个:第一,mid的计算用l + (r - l) / 2,不要用(l + r) / 2,因为当l和r都接近int上限时,l + r可能溢出;第二,更新区间时一定要mid加减一,如果你写成l = mid或r = mid,当l和r差1时就会死循环;第三,while条件用l <= r,意味着搜索区间始终是"左闭右闭"的。
五级考二分还会出"寻找第一个大于等于某个数的位置"这类变体,本质上是一样的,只是把等于的判断条件换成大于等于。所以备考时不要死背模板,要把"为什么这样写不会死循环"的原理搞清楚。
4.3 高精度运算:当long long装不下的时候
高精度运算的原理很简单:C++内置的整数类型有范围限制,long long最多只能表示大约9.2乘以10的18次方,如果题目的数据范围达到10的30次方甚至更大,就只能用数组或字符串来模拟手算过程。五级考高精度主要是加法、减法、乘法,极少考除法。
高精度加法的核心就是把两个数字字符串的每一位逐位相加,注意进位。下面是最常用的实现:
string add(string a, string b) { string res; int i = a.size() - 1, j = b.size() - 1; int carry = 0; while (i >= 0 || j >= 0 || carry) { int digit = carry; if (i >= 0) digit += a[i--] - '0'; if (j >= 0) digit += b[j--] - '0'; res.push_back((char)(digit % 10 + '0')); carry = digit / 10; } reverse(res.begin(), res.end()); return res; }高精度乘法的实现稍微复杂一点,核心是双重循环,逐位相乘然后累加到正确的位置上:
string multiply(string a, string b) { vector<int> res(a.size() + b.size(), 0); for (int i = a.size() - 1; i >= 0; i--) { for (int j = b.size() - 1; j >= 0; j--) { int mul = (a[i] - '0') * (b[j] - '0'); int p1 = i + j, p2 = i + j + 1; // p2是低位,p1是进位 int sum = mul + res[p2]; res[p2] = sum % 10; res[p1] += sum / 10; } } string ans; int start = 0; while (start < res.size() - 1 && res[start] == 0) start++; for (int k = start; k < res.size(); k++) ans.push_back((char)(res[k] + '0')); return ans; }写高精度最容易犯的错是忘记处理前导零。比如0乘以一个大数,结果数组里全是0,如果不跳过前导零,输出就会变成"0000",直接丢分。上面的代码里我用start跳过前导零,这个习惯请务必保留。
5. 真题实操:从一道全排列题看DFS如何完整落地
5.1 题目分析与思路转换
GESP五级真题长期偏好递归搜索,这里我拆一道非常典型、出现在多次模拟和真题中的全排列变式题,帮助你完整走一遍从审题到AC的流程。
给定n个互不相同的正整数,输出这n个数的所有排列,每个排列中的数字不能重复,结果按字典序从小到大输出。
数据范围:1 <= n <= 9。看到n的上限是9,你应该立刻反应过来:全排列数量最多是9! = 362880个,DFS暴力枚举完全能扛住。如果n到20,这题就变成状压DP或者康托展开的考点了,但在五级,DFS就是正解。这也侧面说明五级考试对复杂度分析有一定要求——你得能判断出什么时候用暴力搜索是可行的。
字典序输出这一点不用额外写排序逻辑,只要在DFS枚举时按从小到大选择数字,回溯生成的结果天然就是字典序递增的。这个性质很多学生没注意到,结果多写了一个排序函数,既浪费时间又容易出错。
5.2 完整解题代码与细节注释
直接给出可以提交的AC代码:
#include <iostream> using namespace std; const int MAXN = 15; int n; int a[MAXN]; // 存储输入的数(排好序) int ans[MAXN]; // 当前排列 bool used[MAXN]; // 标记某个下标是否被用过 void dfs(int step) { if (step > n) { for (int i = 1; i <= n; i++) cout << ans[i] << " "; cout << "\n"; return; } for (int i = 1; i <= n; i++) { if (!used[i]) { used[i] = true; ans[step] = a[i]; dfs(step + 1); used[i] = false; // 回溯的核心:撤销标记 } } } int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) { if (a[i] > a[j]) { int t = a[i]; a[i] = a[j]; a[j] = t; } } } dfs(1); return 0; }这个代码有四个细节值得你画圈。第一,used数组标记的是"下标"而不是"值",如果输入的n个数中有重复值,用值做标记会导致重复排列被漏掉;虽然题目说了互不相同,但养成按下标标记的习惯更安全。第二,ans数组下标从1开始,当step > n时说明已经填满所有位置,直接输出。第三,每次输出后要换行,格式错误也是扣分点。第四,回溯时只撤销used[i],不需要撤销ans[step],因为ans[step]在下一次循环中会被覆盖。
5.3 题目变式与扩展:DFS还能这么玩
这道全排列题至少可以朝三个方向变式,都属于五级出题范畴。
第一个方向是组合枚举:从n个数中选m个数,输出所有组合。只需要在DFS参数里增加一个startIndex,让每一层只能从startIndex之后选数,避免选择之前已经考虑过的数,这样就天然杜绝了重复组合。
第二个方向是n皇后问题的简化版:在n行n列的棋盘上放置n个皇后,使它们互不攻击。DFS的每一层对应棋盘的一行,每层枚举皇后放在哪一列,用三个标记数组分别记录列、主对角线、副对角线是否被占用。五级通常只要求判断放置方案是否可行或输出方案数,核心框架和全排列一模一样。
第三个方向是数独填充:每一层枚举当前空格可以填的数字,需要同时检查行、列、宫三个限制条件。这个题的剪枝方式很多,但在五级不要求最优剪枝,只要正确实现基本框架就能得分。
万变不离其宗,只要把DFS框架吃透,这些变式都是换汤不换药。
6. 备考路线图与常见报错排查清单
6.1 从四级过渡到五级:备考时间怎么安排
如果你的四级刚考过,距离五级考试大约有三到四个月时间,我建议按下面的节奏来复习。前一个月集中攻克语言基础,重点是结构体和指针,这部分不牢固,链表和后续的算法就无从谈起。第二个月专门练习递归和DFS/BFS框架,做到看到题目能判断用什么搜索、框架五分钟内能写出来。第三个月主攻应用和查漏补缺,把排序、二分、高精度和链表的代码各写三到五遍,同时开始刷真题和模拟题。
每天建议保持至少一小时的编码时间,周末可以加到两小时。编程能力和游泳一样,光看不练一定不行。我见过太多孩子"我看懂了,但一写就错",原因就是动手量不够。五级不像一级二级那样靠记忆取胜,它必须靠肌肉记忆和调试经验。
6.2 考试环境与编辑器配置:别在起跑线翻车
GESP机试常见于Windows环境,编辑器的选择因人而异,但无论用Dev-C++还是VSCode,考前一定要做两件事:第一,确认编译器的C++标准,最好提前把代码设置成支持C++14或C++17的模式,否则有些语法(比如结构化绑定、auto做函数返回值类型推导)可能编不过;第二,测试标准输入输出的写法,cin/cout加不加ios::sync_with_stdio(false)在数据量大时会有明显差距,建议所有涉及大量输入输出的程序都在main开头加上这两行:
ios::sync_with_stdio(false); cin.tie(nullptr);有些考生在本地用VSCode调试得好好的,一上考试机器不是缺编译器就是环境变量没配好,最后浪费大量时间去折腾环境。这个问题最好的解决方案,是在备考后期刻意去考场用的同一套环境模拟几次机试,提前熟悉。如果实在做不到,至少保证自己能熟练地用一个文本编辑器写代码、用命令行g++编译运行——这个能力在紧急情况下比任何IDE都可靠。
6.3 五级最常见的编译和运行错误速查
我统计了一下我带的学生在五级阶段最常踩的坑,排前三的分别是段错误、栈溢出和未初始化变量。
段错误(Segmentation fault)绝大多数情况下是访问了不该访问的内存,常见于数组越界、链表野指针、空指针解引用。排查技巧是局部缩小法:用注释的手段把代码的一半逻辑屏蔽掉,先确认是哪一段代码触发了段错误,再逐步定位。如果你在写链表操作,优先检查是不是访问了nullptr的next成员。
栈溢出(Stack overflow)通常是无限递归或者递归层数太深。递归层数超过几十万层时,即使逻辑正确也可能爆栈,因为系统栈空间有限。五级的数据范围通常不会逼你写超级深递归,所以出现栈溢出时优先检查终止条件是否写对,有没有可能某个分支永远不会到达边界。
未初始化变量当年的经典翻车现场:定义了一个int变量不赋初值直接用,本地运行碰巧是0,提交到评测机判错或者结果不稳定。C++的局部变量不会自动清零,默认值是不确定的。建议定义局部变量时顺手初始化,比如int cnt = 0; bool ok = false;,这是一个成本极低但能避免大量玄学bug的好习惯。
6.4 考试时的策略建议:拿分优先级怎么排
五级机试的题量和分值设置通常会有梯度,前面一两道偏基础,后面偏综合。我的建议是:拿到试卷先把所有题都看一遍,花五分钟摸清每道题的难度和题型,然后按"先易后难"的顺序做题,千万不要在一道题上死磕。
做完一道题后,先自己构造几个边界测试样例验证正确性。比如排序题测n=1和n=0,高精度题测输入包含前导零或者两个数都是0,搜索题测n=1或棋盘最小尺寸。这些边界样例是最容易暴露隐蔽bug的。
另外,五级考试普遍可以接受"部分正确"的得分,如果某道题你只能写暴力版本,果断写暴力,能拿一部分分就绝不空着。比如一道要求高效算法的题,你就算只会O(n^2)的写法也先交上去,任何得分都优于零分。这个策略听起来很基本,但在考场上很多学生会因为"我只会暴力,觉得太丢人"而放弃,结果一分没拿。
编程考试比的不是谁第一次就完美,而是按规则稳定输出。你写出的程序不需要最优,只需要在数据范围内正确、不超时、不越界——这是五级机试最简单也最容易被忽视的生存法则。