约瑟夫环
题目描述
nn 个人的编号是 1 ~ nn,如果他们依编号按顺时针排成一个圆圈,从编号是 1 的人开始顺时针报数。
(报数是从 1 报起)当报到 kk 的时候,这个人就退出游戏圈。下一个人重新从 1 开始报数。
求最后剩下的人的编号。这就是著名的约瑟夫环问题。
本题目就是已知 n,kn,k 的情况下,求最后剩下的人的编号。
输入描述
输入是一行,2 个空格分开的整数 n,k (0<n,k<107)n,k (0<n,k<107)。
输出描述
要求输出一个整数,表示最后剩下的人的编号。
输入输出样例
示例
输入
10 3
输出
4
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
总通过次数: 2020 | 总提交次数: 2706 | 通过率: 74.6%
难度: 中等 标签: 2018, 规律, 思维, 国赛, 递归
方法思路
为了解决约瑟夫环问题,我们可以使用递推公式来避免模拟过程的高时间复杂度(O(n*k))。递推公式基于以下思路:
-
问题转化:将人的编号从0到n-1(最后结果再加1转回1到n),这样便于数学处理。
-
递推关系:设f(n, k)表示n个人报数到k时最后剩下的人的编号(0到n-1)。当n=1时,f(1, k) = 0。对于n>1,有递推公式:f(n, k) = (f(n-1, k) + k) % n。
-
递推过程:从i=2开始到n,依次计算f(i, k) = (f(i-1, k) + k) % i。这样避免了递归或模拟的高开销。
-
结果转换:最终结果f(n, k) + 1即为原始编号(1到n)
#include <iostream> using namespace std;int main() {int n, k;cin >> n >> k;int ans = 0; // 初始化n=1时的结果(编号0)for (int i = 2; i <= n; i++) {ans = (ans + k) % i; // 递推公式}cout << ans + 1 << endl; // 将编号转回1~nreturn 0; }
代码解释
-
输入处理:读取两个整数
n
(总人数)和k
(报数到k出圈)。 -
初始化:
ans
初始化为0,表示当n=1时剩下的人的编号(0)。 -
递推计算:循环从2到n,每次更新
ans
为(ans + k) % i
:-
ans
:上一轮(i-1人)的存活编号。 -
(ans + k) % i
:当前i人时,存活编号的递推计算。
-
-
结果转换:最终
ans
是0到n-1编号下的结果,加1后转换为1到n的编号输出。