Peter 的烟 的题解
记住只在没有思路时使用题解,不要从它复制粘贴代码。请尊重题目和题解的作者。
在解题之前提交题解的代码会导致封禁。
在解题之前提交题解的代码会导致封禁。
作者:
概述
本题要求计算 Peter 在“ 个烟蒂可以换一根新烟”的规则下,从最初的
根烟出发一共能吸到多少根烟。核心解法是模拟兑换过程:只要手里的烟蒂数不少于
就换烟,把换得的新烟累加到总根数上,再更新剩余的烟蒂数,直到烟蒂数少于
为止。
分析
核心观察
每吸完一根烟都会产生 个烟蒂,所以换来的新烟不需要单独追踪:设当前烟蒂数为
,用它们换到的
根新烟吸完后又会贡献
个烟蒂,加上换剩下的
个,新的烟蒂数就是
。
思路
设总烟数 与手中的烟蒂数
都从
开始,因为最初
根烟都要吸掉,各自产生
个烟蒂。只要
,就执行一次兑换:
其中 是本轮换到的新烟数,
是换完后剩下的旧烟蒂,
是这些新烟吸完后新增的烟蒂。当
时无法再换,此时的
就是答案。
循环一定会终止:当 时,
且
,于是
,而取等号只可能发生在
处,此时新烟蒂数为
,仍然严格小于
,故烟蒂数每轮都在减少。又因为
较大时每轮约缩小为原来的
,轮数为
。
由此还能得到等价的闭式答案:每次兑换净消耗 个烟蒂,循环结束时手中必然剩下
到
个无法兑换的烟蒂,因此一共换得
根新烟,答案也可写作
,可以
直接算出。下面代码采用模拟写法,两者结果完全一致。
具体示例
以 、
为例:初始
、
。第一轮换得
根,于是
、
;此时
,无法再换,答案为
。
再看 、
:第一轮换得
根,
、
;第二轮换得
根,
、
,停止,答案为
。用闭式核对得
,二者一致。
算法步骤
- 读入一组
n与k,若输入已经读完则结束。 - 令
ans与b都等于n。 - 当
b不小于时反复执行:令
x为,把
x累加到ans,再把b更新为。
- 输出
ans,回到第步处理下一组数据。
复杂度分析
- 时间:每组数据
,最坏情况出现在
时,循环次数约为
。
- 空间:
,只用到常数个变量。
实现注意事项
- 题面以“每组测试数据”为单位描述输入输出,一份输入文件可能包含多行、每行一组
与
,需要循环读入直到文件结束,并为每组数据各输出一行。
- 循环条件是烟蒂数不小于
,不能写成大于
:烟蒂数恰好等于
时仍然可以换到
根新烟。
- 更新烟蒂数时要用旧的
同时算出
与
,先求出
再改写
,避免中途覆盖。
- 答案的最大值出现在
、
时,为
,仍在
位有符号整数范围内,不需要更宽的整数类型。
- 题目保证
,烟蒂数每轮严格减少,循环不会陷入死循环,无需额外的步数保护。
- 每组数据的答案都要单独占一行,注意行末换行。
源代码
#include <iostream>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
while (cin >> n >> k) {
int ans = n;
int b = n;
while (b >= k) {
int x = b / k;
ans += x;
b = b % k + x;
}
cout << ans << '\n';
}
return 0;
}
import sys
def main():
data = sys.stdin.read().split()
out = []
for i in range(0, len(data) - 1, 2):
n, k = int(data[i]), int(data[i + 1])
ans = n
b = n
while b >= k:
x = b // k
ans += x
b = b % k + x
out.append(str(ans))
if out:
sys.stdout.write("\n".join(out) + "\n")
main()
评论