NOIP普及组系列【2015】 P2669 [NOIP 2015 普及组] 金币题解
题目背景
NOIP2015 普及组 T1
题目描述
国王将金币作为工资,发放给忠诚的骑士。第一天,骑士收到一枚金币;之后两天(第二天和第三天),每天收到两枚金币;之后三天(第四、五、六天),每天收到三枚金币;之后四天(第七、八、九、十天),每天收到四枚金币……;这种工资发放模式会一直这样延续下去:当连续 n 天每天收到 n 枚金币后,骑士会在之后的连续 n+1 天里,每天收到 n+1 枚金币。
请计算在前 k 天里,骑士一共获得了多少金币。
输入格式
一个正整数 k,表示发放金币的天数。
输出格式
一个正整数,即骑士收到的金币数。
输入输出样例
输入 #1复制
6
输出 #1复制
14
输入 #2复制
1000
输出 #2复制
29820
说明/提示
【样例 1 说明】
骑士第一天收到一枚金币;第二天和第三天,每天收到两枚金币;第四、五、六天,每天收到三枚金币。因此一共收到 1+2+2+3+3+3=14 枚金币。
对于 100% 的数据,1≤k≤104。
问题重述
国王将金币作为工资发放给骑士,第一天1枚,随后两天每天2枚,接着三天每天3枚...即连续k天每天发放k枚金币。给定天数N,求总共发放的金币数。
解法一:模拟递推法
核心思路
通过循环模拟发放过程,维护当前阶段的天数计数和金币面值:
外层循环控制阶段更替(k值递增)
内层循环控制当前阶段的天数执行
实时累加金币并检测剩余天数
代码实现
#include <iostream>
using namespace std;int main() {int N, sum = 0, k = 1;cin >> N;while (N > 0) {int days = min(k, N);sum += days * k;N -= days;k++;}cout << sum << endl;return 0;
}
解法二:数学公式法
核心思路
通过数学推导直接计算:
找到最大的m满足m(m+1)/2 ≤ N
完整阶段金币和 = Σi² (i从1到m)
剩余天数金币 = (N - m(m+1)/2) * (m+1)
代码实现
#include <iostream>
#include <cmath>
using namespace std;int main() {int N;cin >> N;int m = (sqrt(8*N + 1) - 1) / 2;int sum = m*(m+1)*(2*m+1)/6;int remain = N - m*(m+1)/2;sum += remain * (m + 1);cout << sum << endl;return 0;
}
复杂度分析
时间复杂度:O(1)
空间复杂度:O(1)
对比总结
方法 | 优势 | 局限性 |
---|---|---|
模拟递推法 | 逻辑直观易理解 | 大数据量时效率较低 |
数学公式法 | 计算效率极高 | 需要数学推导能力 |
建议初学者先掌握模拟法,再挑战公式法。实际竞赛中根据数据规模选择方法,当N≤1e6时两种方法均可,N≥1e9时优选公式法。
深度总结
这道看似简单的金币问题,实则蕴含着丰富的算法思维训练价值。作为NOIP普及组的经典题目,它完美展现了从暴力模拟到数学优化的思维跃迁过程。
从教学角度来看,模拟法是最符合人类直觉的解决方式。它通过逐日/逐周期模拟金币发放过程,帮助初学者建立问题与计算机实现的桥梁。这种方法虽然效率不是最优,但对于培养编程思维和问题分解能力至关重要。在实际教学中,建议要求学生先掌握这种基础解法,再引导其思考优化可能。
数学公式法则展现了算法竞赛的精髓——通过数学建模将问题抽象化。这里涉及三个关键数学知识点:等差数列求和、二次方程求解和平方和公式。特别是确定完整周期数m时,通过求解m(m+1)/2 ≤ N这个不等式,转化为二次方程求根问题,体现了数学工具在算法优化中的强大威力。
两种解法的时间复杂度对比极具教育意义:模拟法的O(√N)虽然比纯暴力O(N)优秀,但当N达到1e18量级时仍显不足;而公式法的O(1)特性则展现了数学的降维打击能力。这种对比能让学生深刻理解算法优化的价值。
从题目设计角度看,该问题巧妙地将数列知识与编程实现相结合。其变种经常出现在各类竞赛中,比如改为求第N天获得的金币数,或调整发放规则为斐波那契数列等。掌握这类问题的求解范式,对培养竞赛思维很有帮助。
最后需要特别注意的是数值溢出问题。当N较大时(如1e5),平方和可能超过int范围,在实际编程中应该使用long long类型。这也是NOIP比赛中常见的考查点之一。
通过这道题,我们不仅学到了两种具体解法,更重要的是理解了算法设计中的分层思维:从基础实现到数学优化,这是每个选手成长的必经之路。