下面我们来讲解这份2026年9月 CCF-GESP C++ 一级第三部分编程题第2题——《棋盘上的奖赏》。
这道题表面上是在讲一个古老的“国王赏麦子”的故事,实际上是在考同学们一个非常重要的编程思想:
前一个数字是后一个数字的“爸爸”——每次都变成前一个的2倍。
所以这道题的核心就是:
循环 + 每次乘2 + 累加。
题目给出的样例是:输入3,输出7;输入10,输出1023。
一、先来听听“麦粒王国”的故事 🌾👑
很久以前,古印度有一位国王。
有一位聪明的大臣叫西萨,他发明了国际象棋。
国王非常高兴:
“你想要什么奖励?尽管说!”
西萨说:
“我不要金银财宝,只要一些麦子。”
国王一听:
“麦子?这也太简单了!”
于是西萨提出了一个非常特别的要求:
棋盘第1格放1粒
第2格放2粒
第3格放4粒
第4格放8粒
……
也就是说:
每一格都是前一格的2倍。
题目问:
前 N 格一共需要多少粒麦子?
这就是本题。
二、先别急着写代码,我们把棋盘摆出来
假设:
N = 5那么棋盘前5格是:
第1格:1 第2格:2 第3格:4 第4格:8 第5格:16我们把它画成:
🌾 第1格:1 🌾🌾 第2格:2 🌾🌾🌾🌾 第3格:4 🌾🌾🌾🌾🌾🌾🌾🌾 第4格:8 🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾 第5格:16那么总数就是:
1 + 2 + 4 + 8 + 16算一算:
1 + 2 = 3 3 + 4 = 7 7 + 8 = 15 15 + 16 = 31所以:
前5格 = 31粒三、这道题最重要的两个变量
我们不要一上来就写一大堆代码。
先问自己:
程序运行的时候,我到底需要记住什么?
答案只有两个:
①f
表示:
当前这一格应该放多少粒麦子。
②ans
表示:
前面所有格子加起来,一共有多少粒麦子。
可以想象成两个小盒子:
┌──────────────┐ │ f │ │ 当前这一格 │ └──────────────┘ ┌──────────────┐ │ ans │ │ 总麦子数量 │ └──────────────┘四、f一开始应该是多少?
第一格放多少?
题目说:
第1格放1粒。
所以:
f = 1;非常重要!
五、ans一开始应该是多少?
刚开始我们一粒麦子都没有加进去。
所以:
ans = 0;于是程序刚开始:
f = 1 ans = 0可以画成:
当前麦子 f:🌾 总麦子 ans:0六、然后开始一个一个格子处理
假设:
N = 5我们需要处理:
第1格 第2格 第3格 第4格 第5格所以最自然的代码就是:
for (int i = 1; i <= n; i++)翻译成人话:
从第1格开始,一直处理到第N格。
七、第一轮:第1格
现在:
f = 1 ans = 0第1格放:
1所以:
ans += f;相当于:
ans = ans + f;于是:
ans = 0 + 1 = 1然后准备进入下一格。
因为下一格是当前格的2倍:
f *= 2;也就是:
f = f * 2;于是:
f = 1 × 2 = 2现在:
当前格:2 总数量:1八、第二轮:第2格
现在:
f = 2 ans = 1把当前麦子加入总数:
ans = 1 + 2 = 3然后:
f = 2 × 2 = 4现在:
当前格:4 总数量:3九、第三轮:第3格
现在:
f = 4 ans = 3加入:
ans = 3 + 4 = 7然后:
f = 4 × 2 = 8十、第四轮:第4格
现在:
f = 8 ans = 7加入:
ans = 7 + 8 = 15然后:
f = 8 × 2 = 16十一、第五轮:第5格
现在:
f = 16 ans = 15加入:
ans = 15 + 16 = 31然后:
f = 16 × 2 = 32循环结束。
最终:
ans = 31所以答案:
31十二、用表格把整个过程看清楚
这张表特别重要,小朋友考试的时候甚至可以在草稿纸上画出来。
| 棋盘格 | 当前麦子f | 加入后ans | 下一格f |
|---|---|---|---|
| 1 | 1 | 1 | 2 |
| 2 | 2 | 3 | 4 |
| 3 | 4 | 7 | 8 |
| 4 | 8 | 15 | 16 |
| 5 | 16 | 31 | 32 |
你会发现一个非常漂亮的规律:
f: 1 → 2 → 4 → 8 → 16 → 32每次:
×2
而:
ans: 0 → 1 → 3 → 7 → 15 → 31每次:
把当前的
f加进去。
十三、所以代码就出来了
参考程序:
#include <cstdio> #include <algorithm> using namespace std; int n; long long f, ans; int main() { scanf("%d", &n); f = 1; for (int i = 1; i <= n; i++) { ans += f; f *= 2; } printf("%lld\n", ans); return 0; }十四、我们把程序变成“小学生语言”
第1行
int n;准备一个盒子:
n——棋盘有多少格。
第2行
long long f, ans;准备两个“大盒子”:
f → 当前格子的麦子 ans → 所有格子的麦子总数这里特别值得注意:
为什么不用
int,而使用long long?
因为麦子增长得太快了!
十五、为什么麦子数量会“爆炸式增长”? 🚀
看看:
第1格:1 第2格:2 第3格:4 第4格:8 第5格:16 第6格:32 第7格:64 第8格:128 第9格:256 第10格:512看起来前面还挺正常。
但是继续:
第20格:524288再往后:
第30格:536870912再继续:
第40格:549755813888是不是越来越夸张?
这就是:
每次 ×2 的可怕威力。
十六、int可能装不下怎么办?
普通int能表示的整数范围是有限的。
而这道题的麦子增长得特别快。
所以参考程序选择:
long long可以理解成:
🧰 一个比
int大得多的数字仓库。
所以:
long long f, ans;就是为了让程序能够存放更大的麦子数量。
十七、第二个样例:N = 10
题目样例:
输入: 10输出:
1023我们自己算一下:
1 2 4 8 16 32 64 128 256 512全部加起来:
1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512结果:
1023所以:
前10格 = 1023粒十八、这里其实藏着一个数学规律
小朋友如果学过一些数学,会发现:
1 1 + 2 = 3 1 + 2 + 4 = 7 1 + 2 + 4 + 8 = 15 1 + 2 + 4 + 8 + 16 = 31答案分别是:
1 3 7 15 31它们还有一个非常漂亮的规律:
1 = 2¹ - 1 3 = 2² - 1 7 = 2³ - 1 15 = 2⁴ - 1 31 = 2⁵ - 1所以:
⭐ 前 N 格的麦子总数 =2^N - 1
例如:
N = 10 2¹⁰ - 1 = 1024 - 1 = 1023十九、那为什么我们不直接写2^N - 1?
这是一个非常好的问题!
因为对于初学 C++ 的小朋友来说,这道题真正的考点是:
循环。
题目希望我们学会:
for以及:
f *= 2; ans += f;所以不要为了追求“公式”,反而忘记了这道题的编程训练目标。
而且在 C++ 中:
2 ^ n不是数学里的2ⁿ!
这里的^是:
按位异或运算。
所以千万不能直接写:
2 ^ n - 1来表示2^n-1。
这是初学者非常容易踩的坑。
二十、这道题最核心的两句话
如果孩子考试时紧张,记住下面两句话就够了:
🌾第一格麦子是1,所以
f = 1。
🌾每处理完一格,就把当前麦子加进
ans,然后让f乘2。
也就是:
ans += f; f *= 2;这两行是整道题的“心脏”。
二十一、完整程序逐行讲解版
#include <cstdio> using namespace std; int n; long long f, ans; int main() { // 输入棋盘格数 scanf("%d", &n); // 第1格有1粒麦子 f = 1; // 从第1格一直处理到第n格 for (int i = 1; i <= n; i++) { // 把当前格子的麦子加入总数 ans += f; // 下一格是这一格的2倍 f *= 2; } // 输出总麦子数 printf("%lld\n", ans); return 0; }二十二、这道题的“程序思维图”
把整个程序压缩成一张图:
输入 N ↓ 第1格有1粒麦子 ↓ f = 1 ans = 0 ↓ ┌──────────────┐ │ 处理当前格子 │ └──────┬───────┘ ↓ ans += f ↓ f *= 2 ↓ 还有下一格吗? ↙ ↘ 有 没有 ↓ ↓ 继续 输出ans这其实就是一种非常重要的编程模型:
“当前状态 → 加入答案 → 更新状态 → 继续下一轮。”
以后学习很多算法,都会反复看到这种思想。
二十三、孩子最容易犯的5个错误
❌ 错误1:f从0开始
错误:
f = 0;如果这样:
0 → 0 → 0 → 0 → 0永远都是0。
正确:
f = 1;因为第一格就是1粒。
❌ 错误2:忘记f *= 2
如果写成:
ans += f;却忘记:
f *= 2;那么每一格都是1粒:
1 + 1 + 1 + 1 + ...显然不对。
❌ 错误3:先乘2再加
如果写成:
f *= 2; ans += f;第一格就变成:
2但题目第一格明明是:
1所以顺序不能乱。
正确顺序:
ans += f; f *= 2;记住:
先把这一格收进仓库,再准备下一格。
❌ 错误4:ans没有初始化
虽然在这份参考程序中:
long long f, ans;随后直接:
ans += f;但这里有一个 C++ 初学者必须特别注意的知识点:
局部变量如果没有初始化,里面可能是一个未知值。
因此更稳妥、也更适合初学者理解的写法是:
long long f = 1; long long ans = 0;这样我们就明确知道:
当前麦子 = 1 总麦子 = 0❌ 错误5:输出格式写错
因为:
ans是:
long long所以使用printf时:
printf("%lld", ans);而不是:
printf("%d", ans);可以记住:
int → %d long long → %lld二十四、这道题其实是一个“指数增长”启蒙题 🚀
这道题特别有意思的地方在于:
每次只乘2,看起来很慢;可是连续乘很多次,数字会疯狂增长。
比如:
1 2 4 8 16 32 64 128 256 512 1024 2048 4096 ...这也是计算机科学中非常重要的一种增长方式:
指数增长。
所以这道题表面是:
👑 国王给麦子。
实际上是在告诉孩子:
“有些数字虽然开始很小,但如果不断翻倍,很快就会变得巨大。”
二十五、最后给孩子一个“麦粒口诀” 🌾
这道题可以用一句口诀牢牢记住:
第一格,1粒粮;
放进去,再翻倍;
每一格,都累加;
最后得到总麦量。
代码就是:
long long f = 1; long long ans = 0; for (int i = 1; i <= n; i++) { ans += f; f *= 2; } cout << ans;🎯 最后总结:这道题到底考什么?
| 知识点 | 在题目中的作用 |
|---|---|
int n | 保存棋盘格数 |
long long | 保存很大的麦子数量 |
for | 一格一格处理棋盘 |
f = 1 | 第一格有1粒 |
ans = 0 | 一开始总数为0 |
ans += f | 把当前格麦子加入总数 |
f *= 2 | 下一格是当前格2倍 |
printf("%lld") | 输出long long |
这道题最值得孩子掌握的并不是“棋盘麦子”这个故事,而是一个非常通用的累加器模型:
用一个变量记录“当前值”,用另一个变量记录“累计答案”,每循环一次更新当前值,再累计到答案中。