




下面我们来讲解这份 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 |
这道题最值得孩子掌握的并不是"棋盘麦子"这个故事,而是一个非常通用的累加器模型:
用一个变量记录"当前值",用另一个变量记录"累计答案",每循环一次更新当前值,再累计到答案中。