OneCoder Avatar
OneCodercoderli.com · 955 篇博文
一级

C++ 算法考级专栏

真题分析、矩阵探测、递归回溯与基础语法

🎨 视觉封面

【单层循环累加与倍增递推】GESP一级题解:luogu-B4574 [GESP202609 一级] 棋盘上的奖赏

📅 2026-09-15·✍️ OneCoder·计算中...·⏱️ 9 分钟
#GESP#C++#GESP一级#循环结构#递推#累加求和#真题#2026年9月#GESP202609

CCF GESP 2026年9月认证(第十五次认证)C++ 一级编程题第二题,洛谷 B4574。本题取材于数学史与计算机科学中极其著名的“国际象棋麦粒问题(Wheat and Chessboard Problem)”,全面考察了 CCF GESP 一级考纲中的核心考点——单层 for / while 循环结构变量步进累加(Accumulation)倍增递推思想以及整型数据范围防溢出规范。题目从经典的指数爆炸模型切入,兼具趣味性与考纲规范性。题目难度等级为入门(难度评级★☆☆☆☆)。

B4574 [GESP202609 一级] 棋盘上的奖赏

🔗 洛谷原题传送门luogu-B4574 [GESP202609 一级] 棋盘上的奖赏

题目要求

题目描述

相传古印度舍罕王为了奖赏宰相西萨发明国际象棋,西萨提的任何要求舍罕王都愿意满足。西萨只求在棋盘第 1 格放 1 粒麦子,第 2 格放 2 粒麦子,之后每一格都放前一格两倍的麦子,以此类推直到第 64 格。国王觉得这奖赏微不足道,立刻命人取粮。

然而麦子的数量飞速增长:到第 20 格已需上百万粒,到第 40 格就耗尽了全国库存,而后面仍有 24 格。最终国王发现,即便倾尽天下粮食也填不满棋盘。

聪明的你知道前 nn 格总共需要多少粒麦子吗?

输入格式

一行,一个整数 nn

输出格式

一行,一个整数,表示前 nn 格总共需要的麦子粒数。

输入输出样例

样例输入 #1
TEXT
3
样例输出 #1
TEXT
7
样例输入 #2
TEXT
10
样例输出 #2
TEXT
1023

说明/提示

对于 40%40\% 的测试点,保证 1n101 \le n \le 10

对于所有测试点,保证 1n301 \le n \le 30


题目分析与解题思路

1. 麦粒增长规律与数学模型

根据题意,棋盘中每一个格子所放置的麦粒数呈现出经典的等比数列分布:

  • 11 格:11 粒,即 202^0
  • 22 格:22 粒,即 212^1
  • 33 格:44 粒,即 222^2
  • ……
  • ii 格:2i12^{i - 1} 粒。

题目要求计算的是nn 格麦子的总和(即数列前 nn 项和 SnS_n):

Sn=1+2+4++2n1=i=1n2i1S_n = 1 + 2 + 4 + \dots + 2^{n - 1} = \sum_{i=1}^{n} 2^{i-1}

根据等比数列求和公式(首项 a1=1a_1 = 1,公比 q=2q = 2):

Sn=a1(1qn)1q=12n12=2n1S_n = \frac{a_1(1 - q^n)}{1 - q} = \frac{1 - 2^n}{1 - 2} = 2^n - 1

验证样例:

  • n=3n = 3 时:S3=231=81=7S_3 = 2^3 - 1 = 8 - 1 = 7,与样例 1 完全吻合;
  • n=10n = 10 时:S10=2101=10241=1023S_{10} = 2^{10} - 1 = 1024 - 1 = 1023,与样例 2 完全吻合。

2. 一级考纲的教学解法:单层循环累加

在 GESP 一级阶段,核心考查的是基础语句与**单层循环(for 循环)**的编写。我们可以模拟放麦子的全过程:

  1. 定义变量 total = 0,用于累加所有格子的麦粒总数;
  2. 定义变量 current = 1,表示当前格子里应当放置的麦粒数(初始第 1 格为 1);
  3. 循环变量 ii11 循环到 nn
    • 将当前格子的麦粒加到总数中:total += current;
    • 为下一格做准备,麦粒数翻倍:current *= 2;
  4. 循环结束后输出 total

3. 数据范围与整型防溢出核算

题目给出数据范围:1n301 \le n \le 30

  • n=30n = 30 时: S30=2301=1,073,741,8231.07×109S_{30} = 2^{30} - 1 = 1,073,741,823 \approx 1.07 \times 10^9
  • 标准 32 位有符号整型 int 的最大上限约为 2.14×1092.14 \times 10^92311=2,147,483,6472^{31} - 1 = 2,147,483,647)。
  • 虽然 1.07×1091.07 \times 10^9int 上限之内,但在循环更新过程中,若 nn 稍大或中间变量乘 2,极其容易发生符号位溢出。
  • 最佳规范:在信奥与考级中,凡是涉及倍增、幂次或累加求和的问题,一律养成使用 64 位整型 long long 的良好习惯,从根本上杜绝潜在溢出风险。

完整参考代码 (C++11)

CPP
/**
 * Problem: luogu-B4574 [GESP202609 一级] 棋盘上的奖赏
 * Standard: C++11 (CCF GESP 官方大纲推荐标准)
 * Author: OneCoder
 */

#include <iostream>

using namespace std;

int main() {
    // 快速输入输出加速
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    if (!(cin >> n)) {
        return 0;
    }

    // total 存储前 n 格麦粒的总数,current 存储当前格的麦粒数
    // 均选用 64 位有符号整型 long long 防范数值溢出
    long long total = 0;
    long long current = 1; // 第 1 格放入 1 粒麦子

    // 单层循环:依次模拟计算第 1 格到第 n 格
    for (int i = 1; i <= n; ++i) {
        total += current;  // 将当前格麦粒累加至总和
        current *= 2;      // 之后每一格都放前一格两倍的麦子
    }

    // 输出总麦粒数
    cout << total << "\n";

    return 0;
}

方法拓展:位运算极速解法

因为前 nn 格麦粒总和严格等于 2n12^n - 1,在 C++ 中,2n2^n 可以直接借助左移位运算符 1LL << n 进行 O(1)\mathcal{O}(1) 极速计算:

CPP
#include <iostream>

using namespace std;

int main() {
    int n;
    if (cin >> n) {
        // 1LL 表示 64 位 long long 类型的数值 1,左移 n 位即为 2^n
        cout << (1LL << n) - 1 << "\n";
    }
    return 0;
}

注意:必须使用 1LL 而非 1,因为 1 << 30 默认以 32 位整型进行移位,1 << 31 就会产生未定义行为或负数溢出。使用 1LL 可确保 64 位安全移位。


复杂度分析

  • 时间复杂度
    • 循环模拟法:O(n)\mathcal{O}(n)。循环执行 nn 次,在 n30n \le 30 条件下仅执行 30 次操作,耗时小于 0.1 ms0.1\text{ ms}
    • 位运算法:O(1)\mathcal{O}(1)。单条 CPU 移位与减法指令瞬间完成。
  • 空间复杂度O(1)\mathcal{O}(1)。仅开辟两个 64 位标量变量,内存开销可忽略不计。

考点归纳与避坑提醒

  1. 初始值设定:第 1 格麦子是 1 粒而不是 0 粒;
  2. 循环累加顺序:必须先加当前格的麦粒数 total += current,再将当前格乘 2 current *= 2 为下一格做准备,千万不能颠倒;
  3. 整型常量后缀:若使用位运算,牢记 64 位整数字面量后缀 LL
💡 OneCoder 资源指引

所有代码开源上传至 GitHub:yummy-code 仓库 · GESP 专题站:GESP WIKI

🤝 技术交流与答疑

欢迎加入:C++ GESP/CSP 考级答疑群(688906745)Java/Python交流群(982860385),点击可直接加群。

📚

猜你想读 · 相关文章推荐

OneCoder

OneCoder (lihongzheshuai)

一个中年人的自留地,记录学习 C++、GESP/NOI、Java、Python 与算法架构的心得体会。本站唯一网址:coderli.com

💬 读者留言与交流

0 条讨论
✨ 支持 Markdown 语法格式
还没有留言,快来成为第一个讨论者吧!