GESP C++ 三级必备编程程序(真题必背)
| 模块 | 在三级编程题中的地位 | 必背程度 |
|---|---|---|
| 一维数组 + 模拟 | 半壁江山,超过一半的编程题直接用到 | ⭐⭐⭐⭐⭐ |
| 字符串遍历 / 计数 | 第二高频,常与 ASCII、大小写、查找结合 | ⭐⭐⭐⭐ |
| 枚举 + 判断 | 核心算法,没有更复杂算法,关键是定范围与判定 | ⭐⭐⭐⭐ |
| 位运算 + 进制 | 三级独有特色,最容易丢分,套路固定 | ⭐⭐⭐ |
三级编程题共 2 道、每道 25 分、共 50 分,题型就落在这四大块里。下面 7 个"程序骨架"覆盖了近三年绝大多数真题套路——背熟骨架结构 + 易错点,上机直接套即可。
不是死记代码,而是记"套路骨架"。考试会换数据、换表述、换变量名,但模式不变:数组题九成是"遍历 + 标记/计数/极值",字符串题是"遍历 + 判定/计数/反转",位运算是那几个固定套路。理解后比背更稳。
29.1 数组极值替换(数字替换 · 2024.12 真题)
考点:数组遍历 + 找最大/最小值 + 按规则替换(模拟)。
题面模板:给定 个数和一个基准 。把所有 < k 的数替换成序列最小值,把 > k 的数替换成序列最大值,== k 不变;输出替换后的序列。
思路:先扫一遍求 mx/mn,再扫一遍按规则改写。注意极值初值要"极端"。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
int a[N];
int main() {
int n, k;
cin >> n >> k;
int mx = -2e9, mn = 2e9; // 初值必须"极端",不能写 0
for (int i = 1; i <= n; i++) {
cin >> a[i];
mx = max(mx, a[i]);
mn = min(mn, a[i]);
}
for (int i = 1; i <= n; i++) {
if (a[i] < k) a[i] = mn; // 小于 k → 最小值
else if (a[i] > k) a[i] = mx; // 大于 k → 最大值
// 等于 k → 保持不变
}
for (int i = 1; i <= n; i++) {
cout << a[i] << " ";
}
cout << "\n";
return 0;
}
mx/mn初值别偷懒写0——若序列全是负数,mn会错。- 三个分支要用
if / else if / 不变,别漏掉"等于 k 不变"的情况。 - 数据范围 ,数组要开
1e5+5,开小会 RE。
29.2 前缀和判平衡(平衡序列 · 2024.09 真题)
考点:数组 + 前缀和(累加器)。
题面模板:判断数组能否在某处切一刀,使前半部分和等于后半部分和。
思路:前后相等 ⟺ 存在某个前缀和 = 总和的一半。先求总和 sum,再从左到右累加 pre,一旦 pre * 2 == sum 即平衡(至少留一个元素给后半,所以只遍历到 n-1)。
#include <iostream>
using namespace std;
int a[10005];
int main() {
int t;
cin >> t;
while (t--) { // 多组测试数据
int n;
cin >> n;
int sum = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum += a[i];
}
int pre = 0;
bool ok = false;
for (int i = 1; i < n; i++) { // 至少留 1 个给后半
pre += a[i];
if (pre * 2 == sum) { // 前半 = 总和一半 ⟺ 前后相等
ok = true;
break;
}
}
cout << (ok ? "Yes" : "No") << "\n";
}
return 0;
}
- 循环条件
i < n(不是i <= n):后半至少要有一个元素,否则"切"在末尾没有意义。 - 用
pre * 2 == sum代替pre == sum / 2,避免整数除法把奇数总和吞掉。 - 多组输入时,每组都要重置
sum、pre、ok。
29.3 模拟法标准写法(数组清零 · 2025.09 真题)
考点:模拟算法——题目怎么说,代码就怎么写。
题面模板:重复操作直到数组全为 0:找出最大值(多个相等取下标最大的那个,记为 ),在所有非零元素里找最小值,令 a[k] -= 最小值,操作次数 +1。输出总次数。
思路:while 主循环模拟每一轮;找最大值用 >=(相等时保留更大下标,从后往前扫最省事);找最小值要 a[i] > 0 过滤掉 0;终止条件是最大值已为 0。
#include <iostream>
#include <algorithm>
using namespace std;
int a[105];
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
int cnt = 0;
while (true) {
// 找最大值,多个相等取最大下标:从后往前扫,用 >=
int mk = 1;
for (int i = n; i >= 1; i--)
if (a[i] >= a[mk]) mk = i;
if (a[mk] == 0) break; // 已经全 0,停止
// 找非零最小值
int mn = 2e9;
for (int i = 1; i <= n; i++)
if (a[i] > 0) mn = min(mn, a[i]);
a[mk] -= mn;
cnt++;
}
cout << cnt << "\n";
return 0;
}
- 找最大值的比较用
>=而不是>:题目规定"多个最大值取下标最大",从后往前扫恰好满足。 - 找最小值必须
a[i] > 0跳过已清零的元素,否则会减到负数。 - 终止条件是"最大值 == 0",不是"数组长度变 0"。
29.4 字符串计数(词频统计 · 2025.03 真题)
考点:字符串遍历 + 大小写统一 + 计数(计数数组 / map)。
题面模板:给定 个单词,忽略大小写,输出出现次数最多的单词(保证唯一,输出小写形式)。
思路:读入每个单词 → 统一转小写 → 用 map<string,int> 计数 → 边计数边维护最大值。
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main() {
int n;
cin >> n;
map<string, int> cnt;
string best;
int bestc = 0;
for (int i = 0; i < n; i++) {
string w;
cin >> w;
for (char &c : w) // 大写转小写(引用 & 才能改原串)
if (c >= 'A' && c <= 'Z') c += 32;
cnt[w]++;
if (cnt[w] > bestc) { // 题目保证最高频唯一
bestc = cnt[w];
best = w;
}
}
cout << best << "\n";
return 0;
}
- 大小写统一是高频坑:
'A'→'a'差 32,且必须用引用char &c才能改到字符串本身。 - 若只统计 26 个小写字母的频次,可用
int cnt[26] = {0},下标用c - 'a',比map更快也更不容易错。 - 维护最大值要在累加之后比较,顺序反了会漏掉首次出现的最高频词。
29.5 字符串遍历与 ASCII(字母求和 / 字符转换 · 2024.03、2026.06 真题)
考点:字符串遍历 + 字符判定(islower/isupper 或手判)+ ASCII 运算。
题面模板:遍历字符串,对每类字符按规则换算成数值并累加(小写字母常映射为字母序号 c-'a'+1,大写另行处理)。
思路:for (char c : s) 逐字符取;用 c >= 'a' && c <= 'z' 判定类别,再套换算公式。
#include <iostream>
#include <string>
using namespace std;
int main() {
int n;
cin >> n; // 先读个数(也可直接读字符串)
string s;
cin >> s;
long long sum = 0; // 累加器用 long long,防溢出
for (char c : s) {
if (c >= 'a' && c <= 'z')
sum += (c - 'a' + 1); // 小写 → 字母序号(a=1,b=2,...)
else if (c >= 'A' && c <= 'Z')
sum += (c - 'A' + 1); // 大写:按题意决定取正序号或取负
}
cout << sum << "\n";
return 0;
}
- 字符本质是 ASCII 整数,
'a'是 97;求字母序号要用c - 'a' + 1,不是c - 'a'。 - 题目若要求"大写取负值"或"只统计某类",看清规则再写分支。
- 累加结果可能很大,用
long long。
29.6 枚举 + sqrt 判完全平方数(完全平方数 · 2024.03 真题)
考点:枚举(两两组合)+ 完全平方数判定。
题面模板:给定 个数,问有多少对 ()满足 a[i] + a[j] 是完全平方数。
思路:双重循环枚举所有数对;用 isSquare 判完全平方数——k = sqrt(x) 后回判 k*k == x,避免浮点误差。
#include <iostream>
#include <cmath>
using namespace std;
bool isSquare(long long x) {
if (x < 0) return false; // 负数不是完全平方数
long long k = sqrt(x);
return k * k == x; // 用整数回判,绕开浮点误差
}
int main() {
int n;
cin >> n;
long long a[100005];
for (int i = 0; i < n; i++) cin >> a[i];
long long ans = 0;
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
if (isSquare(a[i] + a[j])) ans++;
cout << ans << "\n";
return 0;
}
- 判完全平方数不要只写
sqrt(x) == (int)sqrt(x)——浮点误差会误判。务必k*k == x回判。 - 枚举数对用
j = i + 1避免重复计数与自我配对。 - 数据较大时
a[i] + a[j]可能超int,用long long。
29.7 位运算经典套路(奇偶校验 / 判断 2 的幂 · 三级特色)
考点:三级独有,套路固定,属于"背下来就得分"的送分题。
必背 6 个套路:
#include <iostream>
using namespace std;
int main() {
int x;
cin >> x;
// 1) 判奇偶:看最低位
bool isOdd = (x & 1);
// 2) 判断 x 是不是 2 的幂(x>0 且二进制只有一个 1)
bool isPow2 = (x > 0) && ((x & (x - 1)) == 0);
// 3) 取最低位的 1(lowbit):x & -x
int low = x & (-x);
// 4) 把最低位的 1 清零:x & (x - 1)
int clearLow = x & (x - 1);
// 5) 取第 k 位(从 0 起)是否为 1:(x >> k) & 1
bool bitK = (x >> 2) & 1;
// 6) 统计 1 的个数(Brian Kernighan 法)
int c = 0, t = x;
while (t) { t &= (t - 1); c++; }
return 0;
}
- 奇偶 →
x & 1 - 2 的幂 →
x > 0 && (x & (x-1)) == 0 - 取某一位 →
(x >> k) & 1 - 数 1 的个数 →
while(t){ t &= t-1; c++; } - 奇偶校验题(2025.06)就是反复用
x & 1数最低位、再x >>= 1右移处理下一位。
29.8 三级编程题真题速查表(近三年)
下表来自近 13 场三级真题统计。数组/字符串占编程题一半以上,位运算/进制是三级特色,枚举/模拟是核心算法。背完上面 7 个骨架,基本覆盖表中绝大多数"综合应用"以外的题目。
| 场次 | 编程题1 | 考点 | 编程题2 | 考点 |
|---|---|---|---|---|
| 2023.06 | 春游 | 数组标记+模拟 | 综合应用 | 枚举+判断 |
| 2023.09 | 小杨的储蓄 | 数组+模拟 | 综合应用 | 循环+数组 |
| 2023.12 | 小猫分鱼 | 枚举+倒推 | 单位转换 | 字符串+模拟 |
| 2024.03 | 字母求和 | 字符串遍历+ASCII | 完全平方数 | 枚举+sqrt |
| 2024.06 | 移位 | 位运算+枚举 | 寻找倍数 | 数组+模拟 |
| 2024.09 | 平衡序列 | 数组+前缀和 | 回文拼接 | 字符串+回文 |
| 2024.12 | 数字替换 | 数组+模拟 | 打印数字 | 数组+模拟打印 |
| 2025.03 | 2025 | 数位处理+枚举 | 词频统计 | 字符串+计数 |
| 2025.06 | 奇偶校验 | 位运算+枚举 | 分糖果 | 数组+模拟 |
| 2025.09 | 数组清零 | 模拟 | 日历制作 | 模拟+日期 |
| 2025.12 | 密码强度 | 字符串判定 | 小杨的智能设备 | 模拟 |
| 2026.03 | 二进制回文串 | 进制+位运算+字符串 | 凯撒密码 | 字符串+枚举 |
| 2026.06 | 字符转换 | 字符串+枚举 | 综合应用 | 数组+模拟 |
闭眼能写出来,才算"背完":
- 数组读入后先求
mx/mn再按规则改写 - 前缀和判平衡:
pre*2 == sum且循环到n-1 - 模拟题
while主循环 + 明确的终止条件 - 字符串统一大小写用
char &c引用 - 判完全平方数用
k*k == x回判 - 位运算 6 个套路闭眼默写