GESP C++ 2 级 · 编程题通关手册(教师备课 / 学生背诵用)
GESP 2 级卷面 编程题 2 道 × 25 分 = 50 分,全部考查"多层分支 + 循环嵌套 + 数学函数"的综合运用。把近三年(2023–2026)官方真题抽象后,实际只反复考 7 类套路。把下面 7 个模板背熟、会改,编程题基本必过。
真题来源(洛谷,对应 CCF 官方考试):H 字矩阵 B3924、X 字矩阵 B3865、数位和 B4065、数位之和 B4036、自幂数 T344538、数数 B4497、乘法问题 B3954、小杨做题 B3923、平方之和 B4002。
第20章 GESP 2 级必背编程程序(套路模板)
使用方法:每个模板先 抄写 3 遍 记熟骨架,再把它套到真题里。代码均为完整可编译的 C++17。
20.1 模板一:数位分离与数位和(最高频 ⭐⭐⭐)
真题对应:数位和 B4065、数位之和 B4036、自幂数 T344538、数数 B4497。
套路本质:用 while 循环,反复"取个位 %10 → 累加 → 去掉个位 /10"。
#include <iostream>
using namespace std;
int main() {
long long x;
cin >> x;
int sum = 0;
while (x > 0) { // x 为 0 时直接跳过,sum 保持 0(正确)
sum += x % 10; // ① 取个位,累加到 sum
x /= 10; // ② 去掉个位,向高位推进
}
cout << sum << endl; // 例如 12345 → 1+2+3+4+5 = 15
return 0;
}
记忆口诀
"取余加、整除推,循环到零全拆没。"x % 10 拿个位,x /= 10 削个位,二者顺序不能颠倒(先整除会丢掉个位)。
大数必用 long long:数位和真题里数字可达 10¹²,超出 int,一律 long long。
20.2 模板二:数位统计(数某数字出现次数 / 判定倍数)
真题对应:数位之和 B4036(数位和是 7 的倍数?)、数数 B4497(含恰好 3 个 2?)。 套路本质:在模板一的循环里,把"累加"换成"判断/计数"。
#include <iostream>
using namespace std;
int main() {
long long x; cin >> x;
int cnt = 0; // 统计数字 2 出现了几次
int sum = 0; // 同时算数位和
while (x > 0) {
int d = x % 10; // 当前个位
if (d == 2) cnt++; // 数一数目标数字出现次数
sum += d;
x /= 10;
}
// 美丽数字:数位和是 7 的倍数
if (sum % 7 == 0) cout << "Yes\n";
else cout << "No\n";
// 含恰好 3 个 2: if (cnt == 3) ...
return 0;
}
% 与 / 的优先级
x % 10 和 x / 10 都高于赋值,但写 sum += x % 10; 时一定加括号或用临时变量,避免写成 sum += x % 10; x /= 10; 漏掉第二步导致死循环。
20.3 模板三:字符图形(矩阵打印)⭐⭐⭐
真题对应:H 字矩阵 B3924、X 字矩阵 B3865(以及各类 A/O/E/矩形/对角线)。
套路本质:双层 for(外层行、内层列),在格子 (i,j) 上用 if 判断"它是不是特殊字符",否则输出默认字符。
下面以 H 字矩阵 为例(N 为奇数,最左/最右列是 |,中间行是 -,其余是 a):
#include <iostream>
using namespace std;
int main() {
int n; cin >> n;
int mid = n / 2 + 1; // 中间行(1-indexed)
for (int i = 1; i <= n; i++) { // i:第几行
for (int j = 1; j <= n; j++) { // j:第几列
if (j == 1 || j == n) cout << '|'; // 最左/最右列固定
else if (i == mid) cout << '-'; // 中间行
else cout << 'a'; // 其余
}
cout << endl; // 每行结束必须换行
}
return 0;
}
通用框架(背这个,什么图形都能改):
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == 1 || i == n || j == 1 || j == n) cout << chBorder; // 四边
else if (i == j) cout << chDiag; // 主对角线
else if (i + j == n + 1) cout << chDiag; // 副对角线
else cout << chSpace; // 内部
}
cout << endl;
}
记忆口诀 "图形题,双层圈;特殊格,if 来判;其余格,默认填;每行尾,换行见。"
X 字矩阵只需把"四边"条件换成两条对角线 i==j 或 i+j==n+1 输出 +,其余 - 即可。
20.4 模板四:累加 / 累乘 + 阈值(溢出)判断
真题对应:乘法问题 B3954(乘积超 10⁶ 输出 >1000000)。
套路本质:边乘边判断,一旦超过阈值就标记,之后不再累乘(防溢出)。
#include <iostream>
using namespace std;
int main() {
int n; cin >> n;
long long prod = 1;
bool over = false; // 是否超过阈值
for (int i = 0; i < n; i++) {
long long a; cin >> a;
if (!over) {
prod *= a;
if (prod > 1000000) over = true; // 超阈值即停,避免再乘溢出
}
}
if (over) cout << ">1000000\n";
else cout << prod << endl;
return 0;
}
为什么乘积用 long long:单个 int 乘 int 仍是 int,易溢出。累加/累乘的大数统一用 long long,阈值判断 prod > 1000000 在乘之后做即可(最坏 1e6×100=1e8 仍在 long long 范围内,安全)。
20.5 模板五:数列递推(含停止条件)
真题对应:小杨做题 B3923(第 1 天 a、第 2 天 b,第 3 天起 = 前两天之和;某天 ≥ m 则之后停)。
套路本质:用两个变量滚动保存"前两项",循环里算"当前项",命中停止条件就 break。
#include <iostream>
using namespace std;
int main() {
long long a, b, m; int N;
cin >> a >> b >> m >> N;
long long total = a + b; // 先把前两天的加上
long long p2 = a, p1 = b; // p2=前两天, p1=前一天
for (int day = 3; day <= N; day++) {
long long cur = p2 + p1; // 当天 = 前两天之和(斐波那契变体)
if (cur >= m) { // 达到阈值:当天计入,之后不再做
total += cur;
break;
}
total += cur;
p2 = p1; p1 = cur; // 滚动更新
}
cout << total << endl;
return 0;
}
记忆口诀 "递推题,双变量;当天算,往前滚;达标就 break,别忘加当天。"
20.6 模板六:枚举法(双变量满足条件)
真题对应:平方之和 B4002(是否存在正整数 x,y 使 x²+y² = aᵢ)。
套路本质:数据范围小,直接两层 for 枚举 x、y,命中条件即标记。
#include <iostream>
using namespace std;
int main() {
int n; cin >> n;
while (n--) {
int ai; cin >> ai;
bool ok = false;
for (int x = 1; x * x <= ai; x++) // x 从 1 枚举到 √ai
for (int y = 1; y * y <= ai; y++) // y 同理
if (x * x + y * y == ai) ok = true;
cout << (ok ? "Yes" : "No") << endl;
}
return 0;
}
枚举边界写 x * x <= ai:比 x <= sqrt(ai) 更稳,避免浮点误差;且 x*x 在循环条件里顺便充当平方值,不用再算一遍。注意 x、y 均为正整数(≥1),从 1 开始。
20.7 模板七:擂台法求最值(max / min)
真题对应:数位和 B4065(n 个数中位数和的最大值)。
套路本质:设擂主 ans,每个候选来"打擂",更大/更小就换人。
#include <iostream>
using namespace std;
int main() {
int n; cin >> n;
int ans = 0; // 求最大值:初始给 0(或极小);求最小值给 INT_MAX
for (int k = 0; k < n; k++) {
long long x; cin >> x;
int s = 0;
long long t = x;
while (t > 0) { s += t % 10; t /= 10; } // 套用模板一算数位和
ans = max(ans, s); // 或:if (s > ans) ans = s;
}
cout << ans << endl;
return 0;
}
擂主初始值别乱给
求最大值初始给 0 或 -2e9(数据可能为负时);求最小值初始给 INT_MAX(<climits>)。初始值设错是 2 级高频丢分点。
20.8 真题速查表(背模板 → 套真题)
| 真题(洛谷) | 考点 | 对应必背模板 |
|---|---|---|
| B3924 H 字矩阵 | 字符图形 | 模板三 |
| B3865 X 字矩阵 | 字符图形(对角线) | 模板三 |
| B4065 数位和 | 数位和 + 擂台求最值 | 模板一 + 模板七 |
| B4036 数位之和 | 数位和 + 倍数判定 | 模板二 |
| T344538 自幂数 | 数位分离 + 幂次 | 模板一 + 模板二 |
| B4497 数数 | 数位统计(数某数字) | 模板二 |
| B3954 乘法问题 | 累乘 + 阈值 | 模板四 |
| B3923 小杨做题 | 数列递推 + 停止 | 模板五 |
| B4002 平方之和 | 枚举双变量 | 模板六 |
20.9 通关自检(背完自测)
下面 4 题,请先自己写出完整代码,再展开对照。
- 输入一个正整数,输出它的数位和。
- 输入奇数 N,输出 N×N 的 X 字矩阵(
+在两条对角线上,其余-)。 - 输入 n 个正整数,输出其中数位和最大的那个数位和。
- 输入 a,b,m,N,按"斐波那契变体 + ≥m 停"规则输出 N 天总题数。
参考代码(点击展开)
1. 数位和
#include <iostream>
using namespace std;
int main(){ long long x; cin>>x; int s=0;
while(x>0){ s+=x%10; x/=10; } cout<<s<<endl; return 0; }
2. X 字矩阵
#include <iostream>
using namespace std;
int main(){ int n; cin>>n;
for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++)
if(i==j || i+j==n+1) cout<<'+'; else cout<<'-';
cout<<endl; } return 0; }
3. 数位和最大值(擂台)
#include <iostream>
using namespace std;
int main(){ int n; cin>>n; int ans=0;
while(n--){ long long x; cin>>x; int s=0; long long t=x;
while(t>0){ s+=t%10; t/=10; } ans=max(ans,s); }
cout<<ans<<endl; return 0; }
4. 斐波那契变体 + 停止
#include <iostream>
using namespace std;
int main(){ long long a,b,m; int N; cin>>a>>b>>m>>N;
long long total=a+b, p2=a, p1=b;
for(int d=3; d<=N; d++){ long long cur=p2+p1;
if(cur>=m){ total+=cur; break; }
total+=cur; p2=p1; p1=cur; }
cout<<total<<endl; return 0; }
背熟 7 模板 + 记忆口诀,2 级编程题(50 分)稳拿。 考试时先把题目"翻译"成模板编号,再填空改写,比现场硬想快且不易错。