202503 GESP C++二级编程题复盘:数字拆分、数组标记与字符串边界

📅 发布时间:2026/10/10 11:23:57
202503 GESP C++二级编程题复盘:数字拆分、数组标记与字符串边界
我去年带一个学生考完202503批次的GESP C二级出来以后复盘题目最大的感受是二级真不是“背一背语法就能过”的级别了。考场上三十来道选择判断加四道编程题题目本身看着都不长但设的坑一个接一个尤其是编程大题全是对“能不能把循环、数组、字符串组合起来解决实际问题”的考查。这篇就直接拿202503二级里几个典型题目按考纲风格整理的示例题不是原卷逐字做拆解把每道题背后的考点、思路推导、参考写法、考场翻车点全部讲透。看完以后你会发现二级的编程题是有明确套路的就三类按位拆数字、数组统计排序、字符串边界切分再有就是二维数组的边界处理。这篇适合正在备考GESP二级的学员、带考级的老师以及想拿C入门练代码基本功的自学者。1. 二级考点全景拆解1.1 202503场次的出题风格与难度曲线202503这一批次的试卷选择题部分整体比较温和。变量类型转换、运算符优先级、if和switch选哪个、for循环的执行流程这些是绝对主力。和往年对比这次选择题里“读代码写结果”的题占比明显升高也就是说你光知道语法不够还得能手动模拟程序执行路径。有不止一道题是在键盘上根本输不出正确结果但纸笔推演反而更快的类型。编程大题是拉开差距的关键。四道题从易到难分布大概是第一题纯循环计数第二题是数组标记排序第三题字符串单词处理第四题二维数组边界求和。没有考到太偏的算法也没有考需要递归或者复杂排序算法的题但第四题对“去重”和“边界”的考察很细不少考生栽在这里。整体难度曲线比较友好第一题基本送分第二题认真做能拿大半第三题开始考验代码组织能力第四题是真正的分水岭——能做对的人二级稳过做不对的大概率在及格线边缘。我的判断是202503这套题出得“中规中矩但暗藏杀机”。它不像三级那样考递归、结构体、文件读写这些新东西但把二级考纲里的老考点挖得很深。你如果只是会写单个循环、会定义一个数组但没有把“循环数组字符串”组合起来解决具体问题的能力做题就会觉得每道题都眼熟写起来处处卡壳。1.2 二级考纲核心考点地图要精准备考二级先得在脑子里建一张考点地图。GESP二级覆盖的知识范围大致是基本数据类型与变量作用域、输入输出、if-else与switch分支、for与while循环、一维数组的定义与遍历、字符串的输入输出与常用操作、函数的基本定义与调用以及简单二维数组。二级不考指针、不考结构体、不考文件操作但有一个容易被忽略的要求——代码需要有一定的结构性不能全部堆在main里。从202503的题目来看考纲里有几个点被放大了第一复合条件的判断。比如要统计一个区间里满足多个条件的数每个数都要拆出每一位做判断这就同时用到循环嵌套、整除取模、逻辑表达式。二级的难点从来不是某一个语法点而是多个语法点的组合。第二数组的“标记”用法。很多人学数组只学会了“存数据”和“遍历求和”但二级很爱考“用数组记录状态”——比如某个数字出现没出现过、某个值被使用了几次。这个思想是后面桶排序、哈希表、并查集的地基二级就要求你用最简单的方式体现。第三字符串的边界处理。C里cin遇到空格就停了想读一整行要用getline。而遍历字符串时单词之间的空格、句首句尾的空字符串都是经典坑点。二级的字符串题不会让你做KMP之类的高难度匹配但“怎么把一个句子里的每个单词摘出来”这种基本功是必练的。第四二维数组的角与边。二级大纲里二维数组只要求“初步认识”但202503就在第四题考了矩阵外围一圈求和。这种题代码量不大错起来特别隐蔽四角会被重复计算不画图很容易漏。把这张地图记熟再对照真题逐一击破备考效率会高很多。2. 高频真题逐题精解2.1 数字拆分与条件计数区间特殊数字统计这道题是202503二级的第一道编程大题也是绝大多数考生的送分题。题目大意是给定两个整数L和R统计区间[L, R]内有多少个正整数“不包含数字4且能被3整除”输出满足条件的数字个数。从考点上看这道题同时覆盖了循环遍历、按位拆分数字、取模判断三个核心技能。拿到题的第一反应不是立刻写代码而是把条件拆成两条一是数字十进制表示里没有任何一位等于4二是这个数整体能被3整除。参考代码可以这样写#include iostream using namespace std; bool hasDigit4(int n) { while (n 0) { if (n % 10 4) { return true; } n / 10; } return false; } int main() { int L, R; cin L R; int cnt 0; for (int i L; i R; i) { if (i % 3 0 !hasDigit4(i)) { cnt; } } cout cnt endl; return 0; }我特意把“判断一个数含不含4”这个逻辑封装成了函数hasDigit4而不是全部写在main里面。这样做的原因有两点一是代码可读性更高main里面只剩循环和条件阅卷人或者你自己回看代码时一眼就能懂二是这个逻辑本身是独立的抽成函数以后如果后续题目改成“不含5且不能被7整除”之类的变体直接改函数名和判断条件就行不用动main里的结构。按位拆数字的原理很简单对10取余拿到个位再除以10把个位挤掉。比如n345第一次n%10得到5n/10后n34第二次得到4n3第三次得到3n0循环结束。整个过程相当于把数字从右往左剥洋葱每一层都是当前的最低位。这里需要注意一个细节如果n本身就是0while循环一次都不会执行直接返回false。题目里L至少是1但这个函数的通用性对边界情况要心中有数。如果用一个更巧妙的写法可以把数字转成字符串然后用字符串查找来判断有没有字符’4’。这种写法在代码量上更短但运行速度差不了多少而且字符串处理的思路在第三题会用到。考试时用哪种都行关键是稳定不犯错。我推荐基础阶段用取模拆位法因为它不依赖额外的头文件和字符串API思路直接贴合“数字”这一层语义。这道题的隐藏考点是复杂度。L和R的范围题目没给到特别大一般不超过100000直接用for循环逐个判断单次判断的耗时是数字位数级别整体完全没问题。但如果有人自作聪明想用数学公式去算反而容易算错还浪费考场时间。2.2 数组标记与去重输出成绩统计这题是202503二级的第二道编程题考查数组的标记用法和排序思路。题目大意是班上有n个同学参加模拟测试每个同学有一个整数成绩范围0到100现在要把所有不同的成绩按从高到低输出并且统计不同成绩的数量。这题有两个关键点第一是“去重”第二是“从高到低排序”。C里最直接的做法是定义一个大小为101的桶数组用成绩作为下标记录这个分数是否出现过。因为成绩的范围已经限制在0到100桶数组的开销极小这种思路既简单又不容易错。参考代码#include iostream using namespace std; int main() { int n; cin n; bool appeared[105] {false}; for (int i 0; i n; i) { int score; cin score; appeared[score] true; } int cnt 0; for (int s 100; s 0; s--) { if (appeared[s]) { cnt; cout s ; } } cout endl cnt cnt endl; return 0; }用bool数组当“标记位”是这一题的灵魂。appeared[score] true意思就是“这个分数出现过”。全部读完后从100往下扫描遇见true就输出天然就是降序根本不用写排序算法。整个过程的时间复杂度是O(n101)空间复杂度O(101)在考试环境下几乎可以认为不耗资源。这个思路我建议所有考二级的人都练熟因为它是后面学习桶排序、计数排序的前置铺垫。很多人一看到“排序”两个字立刻想到冒泡或者选择排序但在这个场景里数据范围小且需要去重桶的做法比排序算法更贴合题意。如果你对STL的sort和unique比较熟悉也可以写#include algorithm int arr[105]; // 读入n个数到arr sort(arr, arr n); int len unique(arr, arr n) - arr; for (int i len - 1; i 0; i--) { cout arr[i] ; }这种写法需要自己控制数组长度和unique的返回值一旦搞混容易输出多余的元素。如果对STL不够熟练考场我更推荐用桶标记的方式直观、不易错、代码也短。这道题还有一个容易忽略的输出细节——题目可能对输出格式有要求比如最后一个数字后面有没有空格、输出顺序是先输出数量再输出列表还是反过来。读题时一定把输出格式圈出来很多满分代码就栽在这个小地方。2.3 字符串单词切分句子反转第三题是202503二级里字符串题的典型代表。题目大意是输入一句英文句子单词之间用若干个空格分隔要求把每个单词里的字母顺序颠倒但单词在句子中的顺序保持原样。输出处理后的句子。这题考的其实是“字符串的边界切分”和“子串的逆序输出”。标点符号不参与处理假设输入只包含大小写字母和空格。很多考生第一反应是先把整个句子反转再把每个单词反转回来——这是经典的双重反转法适用于整个字符串逆序且单词内部顺序也要维持的场景但这里要求单词内部颠倒、单词顺序不变所以直接用一次“按单词逐字操作”更容易想清楚。参考代码#include iostream #include string using namespace std; int main() { string line; getline(cin, line); string result ; int n line.length(); for (int i 0; i n; ) { if (line[i] ) { result ; i; continue; } string word ; while (i n line[i] ! ) { word line[i]; i; } // 逆序输出这个单词 for (int j word.length() - 1; j 0; j--) { result word[j]; } } cout result endl; return 0; }这里有个很多人不知道的细节cin s读入字符串时会跳过开头的空格遇到空格就停下来根本读不进包含空格的完整句子。所以句子输入必须用getline(cin, line)。string类的length()方法返回的是字符串长度注意遍历时别把下标越界。在写循环的时候我是用“指针式扫描”的思路维护一个下标i从0往后走。遇到空格说明单词之间的分隔符直接在结果里补一个空格遇到字母就进入内层while循环把连续的字母攒成一个word然后逆序追加进result。等内层循环退出时i要么走到了字符串末尾要么停在了空格前外层循环会继续处理。这种写法可以应对多个连续空格的情况因为每个空格都会单独执行一次result加空格不会把多个空格吞成一个。有同学用cin逐词读入再反转每个词遇到连续空格时输出格式就对不上这就是丢分点。从复杂度看每个字符最多被处理两次一次加进word一次逆序输出时间O(n)空间O(n)。n是整句长度二级场景下完全没问题。做这类题时我建议先在草稿纸上模拟一次比如输入“I love GESP”肉眼过一遍I逆序还是Ilove变evolGESP变PSEG输出“I evol PSEG”再对照代码逻辑基本不会错。2.4 二维数组边界条件矩阵外围求和第四题是202503二级的压轴编程题考查二维数组的遍历和边界去重。题目大意是输入一个n行m列的整数矩阵计算矩阵最外一圈所有元素的和并输出。这题看起来简单陷阱却不少。最直观的做法是写两重循环遍历所有元素判断当前元素是否在边界上是就累加。但判断“边界”条件本身就有讲究行下标为0或n-1或者列下标为0或m-1这两个条件用或连接即可。参考代码#include iostream using namespace std; int main() { int n, m; cin n m; int a[105][105]; for (int i 0; i n; i) { for (int j 0; j m; j) { cin a[i][j]; } } int sum 0; for (int i 0; i n; i) { for (int j 0; j m; j) { if (i 0 || i n - 1 || j 0 || j m - 1) { sum a[i][j]; } } } cout sum endl; return 0; }这种全遍历判断边界的方式优点是逻辑简单不容易漏元素缺点是每个位置都判断了一次但n和m通常不超过100性能完全不是瓶颈。另一种写法是分四条边分别累加先加第一行、最后一行再加第一列、最后一列但这样四个角落会重叠必须在循环里跳过已经加过的位置很多考生就错在重复计算四个角。我推荐的还是“遍历时判断边界”的统一处理思路只写一个if不做特判。边界条件里还有一个坑当n1或m1时矩阵只有一行或一列“外围”就等于整个矩阵所有位置都满足边界条件统一判断法依然成立。但如果是分四条边相加的写法这时的去重逻辑就很难处理。所以考场遇到这种题选择遍历判断法更稳。这道题背后考查的是“用二维下标描述一个区域的位置关系”。你能把矩阵想象成格子图行方向是纵轴、列方向是横轴边界就是四条边和四个顶点。二级不要求你写DFS或BFS这类图论算法但这种“把现实问题转成坐标判断”的模型是后面算法学习的重要基础。3. 易错点与避坑指南3.1 历年考生最容易栽的几个坑带学生复盘了多年二级真题我总结出几个反复出现的错误模式。第一个就是**“循环边界差一”**。for (int i 0; i n; i)里面到底是i n还是i n遍历数组下标从0开始最后一个元素的下标是n-1所以必须写成i n。如果用i n就会访问a[n]越界。这个问题在数组题和矩阵题里出现频率极高。第二个是整数除法与取模的混淆。比如判断一个三位数是不是水仙花数需要拿到百位、十位、个位。百位是n / 100十位是(n / 10) % 10个位是n % 10。这几步里只要有一个取模或除法的位置写反结果就完全错。不少考生把十位写成n % 100得到的是后两位不是十位。做题时先在草稿纸上写清每一位的表达式再上机。第三个是输出格式错误。GESP的判分系统对输出有严格的格式要求末尾多一个空格有时候不算错但要求换行的地方没有换行有时候直接扣分。有些题明确要求“每个数之间用空格隔开”你用cout x endl就会把后面的数换到下一行。考场最好把样例输出的格式一模一样的复刻一遍特别是处理完最后一个数后不要多输出空格。第四个是不写return 0。很多人觉得main函数结束自动返回没有写return 0的习惯。在Xcode或VS Code的本地环境里可能不报错但在评测系统里某些编译器会给出警告甚至非零退出码导致程序被判运行时错误。二级养成好习惯main函数最后一定写return 0。3.2 测试点设计与自测策略很多考生平时做题只测样例数据样例过了就提交结果一上评测机就崩。原因是样例数据往往比较简单根本覆盖不到边界情况。我建议每道编程题都至少准备五组自测数据正常数据、最小值数据、最大值数据、重复数据、特殊形状数据。拿矩阵外围求和题举例。样例一般给3行4列这种常规数据你还要测n1、m1的情况再测1行5列再测5行1列。这些特殊形状下边界判断条件是否仍然成立只有测了才知道。再比如区间统计题L1、R100000是最大范围L1、R1是单元素区间L100、R100是区间首尾相等这些都必须测。自测的方法是准备一个最小测试用例然后用“人工推演一遍程序路径”的方式验证结果。比如输入“3 3”的矩阵1 2 3 4 5 6 7 8 9外围和是1236987440。你跟踪代码的循环i0时j0,1,2全部累加i1时j0和j2累加j1不满足边界条件不加i2时j0,1,2全部累加。最后结果40和手算一致。这种用手动推演确认代码逻辑的过程比直接提交然后反复修改试错要高效得多。还有一个实用的自测技巧在本地写代码时用一组“极端数据”去跑把中间过程用cout打印出来比如统计题里把每个满足条件的数打印出来看看跟你的判断是否一致。调通之后再把调试输出注释掉或删掉提交干净的代码。这个习惯保持下来最终上考场的时候犯错的概率会大大降低。4. 考场实战与能力进阶4.1 二级考场上拿到编程题后的操作顺序我见过不少学生拿到编程题就急着敲代码也不读完整题目结果敲到一半发现理解错了又删掉重来白白浪费半小时。正确的顺序应该是读题三遍、划条件、定思路、写注释框架、再填充代码。第一遍快速浏览题目大意第二遍把输入范围、输出格式、特殊要求划出来第三遍在心中用人话复述一遍题目确认没有歧义。如果一道题读完三遍还没思路不要硬抠。先跳过做下一道把会做的稳定拿到分再回头啃难题。二级的编程题一般是按难度排序的但偶尔也有“第一题文字特别长”的情况这时候你更要沉着。核心的得分策略是四道编程题争取前两道全对第三道拿一大半第四道写出暴力版本拿部分分。部分分在GESP判分里是有的只要思路对、能处理部分数据评测机就会给对应的分值所以“不会做也要写一个能处理小数据的版本”这一条非常重要。上机的时候还要养成随时保存的习惯。虽然评测系统一般不会强制关闭但考试紧张状态下误操作清空代码的情况不是没发生过。每写完一个函数就按一次CtrlS写完一道题就整体提交一次确保至少有一版能跑通的代码在系统里。4.2 从二级到三级下一步怎么练二级通过以后很多学生会问我下一步怎么衔接三级。GESP三级开始考递归、结构体、函数进阶和更复杂的一维数组应用思维难度是跳跃式的。我建议在二级备考刚结束时不要立刻刷三级真题而是先用一周时间把二级里面不够熟练的点补扎实特别是函数的封装和二维数组的操作。基本功稳了三级的学习才会事半功倍。三级最常出现的“递归”其实并非天外飞仙它就是“函数调用自己”。学递归前最需要的是把函数调用的栈过程搞明白调用时有哪些参数压栈返回时怎么带回结果。你可以用一个简单的小例子——计算阶乘——在纸上手动模拟每一次调用和返回画一个栈的进出图这个图画明白了递归就入门了。三级的结构体则是对“数据打包”的需求可以把它理解成把多个相关变量捆在一起相当于给数据建模二级里的数组和它是一脉相承的。对还在二级备考阶段的同学我的建议是每周至少完整写四道编程题每道题都用“自主设计测试数据、手写推演、上机验证”的流程过一遍。编程不是一个靠“看懂”就能学会的技能它像骑自行车看再多视频不如自己蹬两圈。等你把202503这种难度的题目写熟到“看到题就能分出考点”的程度二级证书基本就是囊中之物了。我自己的体会是GESP二级这个位置其实很妙——它不像一级那样只需要会输出Hello World也不像三级那样要啃递归大山而是刚好卡在“你会不会用语言解决问题”这个分界点上。这次202503的题再次印证了这一点没有偏题怪题但综合度比往年微调了字符串题和矩阵题的分量在加重。备考时与其迷信押题不如把每一类题的基本功练扎实。最后分享一个小技巧每道题AC之后花五分钟重新审视自己的代码想想哪些地方可以抽成函数、哪些边界可以写得更严密。这个习惯比多刷十道新题更有价值。