刷过GESP真题库的人,基本都见过B4409这一题。热搜里“gesp一级备考”“gesp二级”这些词常年挂在榜上,家长和学生都在找真题、找解析,但很多人第一反应是:“商店折扣?这也太简单了吧,不就是算个打折吗?”实际带学生跑一圈下来,我发现恰恰是这种题最能暴露问题。一个班十个人做这道题,能一次通过的往往不到一半。不是因为不会判断打折,而是输出去、边界、类型、格式化这几个环节总有一个地方出错。
GESP一级这门考试,考的是“从零开始学编程一个学期左右”的水平。也就是说,它不要求你会数组、不要求你会递归、不要求你会任何算法,它只要求你做到三件事:读得懂题目、写得对分支循环、输得出符合要求的格式。听起来容易,但真正支撑这三件事的,是几十个细碎的知识点。B4409这题表面在考打折,其实一次性覆盖了其中最重要的四个:条件判断、浮点数、边界处理和格式化输出。
从这个角度看,它就是一级考纲的浓缩版。这也是我把这道题单独拿出来讲的原因——不是因为它难,而是因为它的信息密度足够大,把这一题吃透,比盲目刷二十道同类题更管用。
1. 一道看似简单的一级题,为什么值得单独拿出来讲
1.1 GESP一级考纲与这道题的关系
先看一级通常考什么。读入用 cin 或 scanf(Python 用 input),输出用 cout 或 printf(Python 用 print);变量分整数和浮点数;算术表达式要知道运算优先级;关系表达式掌握大于、小于、大于等于这些比较;然后是 if-else 分支,for 和 while 循环。整套考点加起来,其实就对应着“把生活问题翻译成程序”的基础能力。
商店折扣正好长在“生活问题翻译”这个点上。原价多少钱、满足什么条件打折、折后是多少、输出要保留几位小数,每一句话都要翻译成代码。一级大多数题目都是这个套路,只是换了场景。
1.2 题号里的信息:B4409和GESP202509
B4409 这个编号,不同平台有不同的编法。我理解 B 一般代表基础题,4409 是题号;后面的 GESP202509 表示这是 2025 年 9 月那场 GESP 认证的一级真题。真题的可贵之处在于,它的测试数据是认认真真设计过的,边界情况、格式要求都经过了校验,比网上很多随手出的模拟题严谨得多。所以备考阶段,真题至少要过两遍:第一遍按考试状态做,第二遍拆开分析它到底考了什么。
GESP 一级可以用 C++ 或 Python 作答,具体看自己更熟悉哪个。C++ 的代码更强调类型和格式,Python 的代码更强调逻辑简洁。两种语言我都会给例子,因为考点是一样的:语言只是工具,思路对了,写哪个都能过。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 原题还原与样例手推:先当数学题,再做编程题
2.1 我拿到的题目版本
我印象中这道题大致是这样的,各平台回忆版可能略有差异,但核心考点一致:
某商店开展打折活动。输入一个正整数 price,表示商品原价。如果 price 大于等于 100,则按原价的八折收费;否则按原价收费。请输出应付金额,保留一位小数。
输入:一个正整数 price(范围一般会在 1 到 1000 之间)。
输出:应付金额,保留一位小数。
样例1:
输入:100
输出:80.0
样例2:
输入:45
输出:45.0
当然,不同批次的版本可能把折扣条件改成“满 50 打七折”或者“输入两个数:原价和折扣数”,但核心逻辑不会变:先判断,再计算,最后格式化输出。收到题之后先别急着写代码,把题目里的打折条件、折扣力度、输出格式这三样东西圈出来。
2.2 手工推演两个样例
样例1:price = 100。100 >= 100 成立,所以进入打折分支。八折就是原价乘以 0.8,100 * 0.8 = 80。输出不能写 80,因为题目要求保留一位小数,所以写 80.0。注意这里不是“多少位有效数字”的问题,就是小数点后固定一位。
样例2:price = 45。45 >= 100 不成立,走原价分支,应付 45。但题目要求保留一位小数,整数 45 也要写成 45.0。这一步是很多初学者第一次犯错的地方:以为只有打折才需要小数,其实不打折也要按输出格式来。
2.3 把生活语言翻译成代码语言
“打八折”在生活中就是“乘以 0.8”,或者写成“乘以 80 除以 100”。用代码表达,就是 price * 0.8。这个翻译过程是小学高年级就能做的数学题,但放到程序里就有讲究:price 是整数,0.8 是浮点数,二者相乘,C++ 会自动把结果提升为浮点数,所以答案不会丢小数;但如果你图省事写 price * 80 / 100,两个整数先乘再除,在某些边界上会触发整数除法,结果可能不对。这一点我放在第 4 部分专门讲。
“满足条件”在生活中是“满 100 就打折”,翻译成关系表达式就是 price >= 100。注意是大于等于,不是大于。很多同学背熟了 if 的写法,却在写条件时把 >= 和 > 搞混。程序不会猜你的意图,条件错一个符号,整道题就错了。
2.4 保留一位小数的本质
保留一位小数,本质是“格式化输出”,不是“四舍五入计算”。计算结果是 80 还是 80.0,数值大小一样,但输出字符串是否符合要求,判题机看得一清二楚。
C++ 里常见写法是 printf("%.1f", ans) 或者 cout << fixed << setprecision(1) << ans;Python 里是 print(f"{ans:.1f}")。这几种写法都是在做格式化,不是在做数学运算。这条认知非常关键,因为判题系统用文件比对输出结果,多一个 0 少一个 0 都会直接判错。
3. 几种实现写法与它们背后的C++基本功
3.1 最朴素的 if-else 写法
cpp复制#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int price;
cin >> price;
double ans;
if (price >= 100) {
ans = price * 0.8;
} else {
ans = price;
}
cout << fixed << setprecision(1) << ans << endl;
return 0;
}
这段代码逻辑非常直白,我建议一级考生把这份代码练到默写级别。用到的东西:cin 读整数、if-else 分支、double 声明变量、cout 配合 fixed 和 setprecision(1) 格式化输出。里面有两个小细节:一是 ans 声明在 if 外面,这样后面输出的时候变量仍然存在;二是 price * 0.8 是整数乘浮点数,结果是浮点数,所以塞进 double 没问题。
3.2 用三目运算符压缩代码
cpp复制#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int price;
cin >> price;
double ans = (price >= 100) ? price * 0.8 : price;
cout << fixed << setprecision(1) << ans << endl;
return 0;
}
三目运算符的格式是“条件 ? 真值 : 假值”,它把 if-else 的分支压缩成一行。这种写法简洁,但一级阶段我不强求。如果你已经熟悉 if-else,三目运算可以作为一种“看得懂”的补充。反过来,如果三目运算看着别扭,完全可以用 if-else,判题不看你代码风格,只看结果。不过,学会三目运算对以后读别人的代码有帮助,题库的题解经常出现这种写法。
3.3 避免浮点数的整数运算技巧
有同学会想:八折就是 price * 80 / 100,能不能全用整数算?可以,但要非常小心。C++ 里两个整数相除结果还是整数,小数部分直接丢掉。如果写成 price * 80 / 100,因为先乘后除,price * 80 通常能得到比较大的数,最后除以 100 取整。
比如 price = 100,100 * 80 / 100 = 80,没问题;price = 45,45 * 80 / 100 = 36,也对,因为 45 不打折,应该走原价分支输出 45。所以整数写法也得先判断分支,只在打折分支里用。
但如果把顺序写成 price / 100 * 80,就出大事了。price / 100 先把两个整数相除,price = 100 时结果是 1,1 * 80 = 80,碰巧对;price = 120 时,120 / 100 = 1,1 * 80 = 80,而正确值应该是 96。这就是运算顺序带来的问题。所以哪怕要用整数,也要先乘后除,并且明确自己在哪个分支里。对本题,既然结果要保留一位小数,最省心的还是用浮点数。
3.4 Python 版本做对照
python复制price = int(input())
if price >= 100:
ans = price * 0.8
else:
ans = float(price)
print(f"{ans:.1f}")
注意 else 分支里我用 float(price) 把整数包了一下,目的是让 ans 在两条分支里都是浮点数,避免类型不一致在 print 格式化时出状况。其实不转换也可能能输出对,但显式转换是一种好习惯。f"{ans:.1f}" 是 Python 里最直观的保留一位小数写法。
几种写法放一起对比如下:
| 写法 | 优点 | 风险 |
|---|---|---|
| C++ if-else | 逻辑清晰,适合考试 | 代码略长 |
| C++ 三目 | 简洁 | 不熟容易读错 |
| C++ 整数运算 | 无浮点精度问题 | 运算顺序错就翻车 |
| Python f-string | 语法直观 | 依赖 Python 环境 |
用什么语言不重要,关键是把一份代码的思路吃透。
4. 实际提交时最容易丢分的三个细节
4.1 输出格式:fixed 与 setprecision 的位置
C++ 输出保留一位小数最标准的组合是:
cpp复制cout << fixed << setprecision(1) << ans;
很多初学者会写 setprecision(1) 但漏掉 fixed。这样意思变成“保留 1 位有效数字”而不是“保留 1 位小数”。当 ans 是 96.0 这种值时,不加 fixed 的输出可能会变成科学计数法形式或有效数字形式,反正不是你想要的 96.0。一级题虽然数值不夸张,但养成“格式化输出必须写全”的习惯,对后面的二级三级考试非常有帮助。
用 printf 的话,对应的是 printf("%.1f", ans)。这个格式串里,.1 表示小数点后 1 位,f 表示按浮点数输出。注意 printf 需要包含头文件 <cstdio>。用 cout 流派还是 printf 流派,看个人习惯,但别混着用,混用会容易忽略缓冲区问题,考试时增加不必要的风险。
4.2 边界条件:包括等于和不包括等于
判题数据一定会测边界。如果题目说“price >= 100 打八折”,那么 price = 100 必须走打折分支,输出 80.0。你要是写成了 price > 100,price = 100 这组测试点直接挂,而且你大概率发现不了,因为样例里未必有 100。这就是真题和模拟题的区别:真题专门设计隐藏边界测试,专治粗心。
应对办法是在草稿纸上手动算边界值。拿到题先圈出条件里的“大于等于”“大于”“小于”这些词,再把这个边界数代进程序走一遍。这道题的边界就是 100,另外输入范围的两个端点 1 和 1000 也顺手测一下。三个边界值测完,整道题的稳定性已经超过大部分人了。
4.3 整数除法的坑再强调一遍
回到整数运算那个话题。C++ 中,5 / 2 = 2,不是 2.5,因为 int 和 int 相除结果还是 int,小数被无情截断。本题里如果写 ans = price * 80 / 100,并且 ans 是 int,那么 price = 120 时,120 * 80 / 100 = 96,没问题;但如果折扣是“六五折”这种,原价 * 65 / 100 在很多情况下会丢掉小数,题目又要求保留一位小数,那就直接错了。
所以处理折扣这类题,只要题目里出现小数或保留位数,第一反应就是用浮点数。整数运算技巧可以作为知识储备,但不是考试的首选方案。
还有一个隐藏问题:输入类型。题目说输入正整数 price,那用 int 接收没问题。如果有人用 double 接收,虽然也可能通过,但会引入浮点比较的隐患。一级阶段就按整数读入,不要给自己加戏。
4.4 题目变体:输入个数和折扣条件可能不一样
题库里“商店折扣”这类题有很多变体。有的版本输入两个整数,第一个是原价,第二个是折扣数(比如 7 表示七折),让你直接算折后价;有的版本输入一个整数,再根据是否满 100 决定打九折还是打九五折。遇到变体,核心读题方法不变:确定折扣条件、确定折扣力度、确定输出格式。
我见过有同学把“输入两个数”默认当成“输入一个数”,结果只 cin 了一个变量,另一个数留在缓冲区里,程序提前结束,判题直接超时或报错。所以每次写题之前,先看输入那一行写的是几个变量,别靠猜。
5. 把这道题吃透之后:GESP一级备考的几条实用建议
5.1 固定一套“读题-手推-编码-复核”流程
很多同学刷题是看到题直接上手敲代码,敲完发现不对再重试,非常浪费考试时间。我建议把做题流程固定成四步。
第一步,读题。不看代码,把输入变量、条件阈值、输出格式三样东西写在草稿纸上。第二步,手推。至少把题目给的两个样例用数学方法算一遍,确定预期输出。第三步,编码。按照手推的逻辑写代码。第四步,复核。拿样例测试,再换边界值测试。
这套流程看着慢,实际最快。一级题的代码量都小,真正的耗时都花在“一开始理解错了”然后回头改的循环里。流程固定之后,一次过的概率会大幅提升。
5.2 按考点分层刷题,而不是按难度随机刷
一级备考最忌讳“看到哪题刷哪题”。我建议按顺序刷三批题。第一批,顺序结构题,练输入输出和变量类型,目标是让 cin、cout、int、double 这些基础词汇变成肌肉记忆。第二批,分支结构题,练 if-else 和关系表达式,每道题都手动计算边界值。第三批,循环结构题,练 for 和 while,注意循环变量从 0 开始还是从 1 开始。
商店折扣这道题属于第二批,但它涉及浮点数和格式化输出,其实是第二批里最有代表性的题目之一。把它彻底弄懂,比刷十道同质化的“比大小”题更有价值。
5.3 准确度比速度重要,输出格式比思路重要
一级考试的时间通常比较充裕,很多学生不是不会做,而是输错格式被扣分。一个保留小数位数没写对,可能扣掉整道题的分。我的建议是:平时练习就养成只看题目要求、不看样例猜输出的习惯。样例只是参考,不是标准答案的全部。判题系统看的是字符串匹配,你少一个“.0”就是错。
另外,代码提交前做两件事:检查输出行有没有换行,检查变量类型和格式化函数是否匹配。这两样占了一级考试丢分原因的很大比例。
5.4 刷题数量之外的真实看法
我不太赞成“刷够 300 题就稳过一级”这种说法。一级考点就那么几十个,关键是每个考点是否真的理解,而不是见没见过。如果商店折扣这种题你能做到手推、编码、自查一步到位,说明你已经具备了过一级的核心能力。接下来再去接触二级的循环嵌套、简单数组,基础就会特别扎实。
我这几年带学生的体会是,一级能不能过,看的不是智商,而是是否认真。认真读题、认真格式化、认真测边界,这三件事做到了,一级基本就是送分等级。而这一切,都可以从 B4409 商店折扣这一个小题开始练起。
