蓝桥杯约瑟夫环(递推)

1. 问题描述:

n 个人的编号是 1 ~ n,如果他们依编号按顺时针排成一个圆圈,从编号是 1 的人开始顺时针报数。(报数是从 1 报起)当报到 k的时候,这个人就退出游戏圈。下一个人重新从 1 开始报数。求最后剩下的人的编号。这就是著名的约瑟夫环问题。本题目就是已知 n,k 的情况下,求最后剩下的人的编号。

输入描述

输入是一行,2 个空格分开的整数 n, k (0 < n,k < 10 ^ 7)

输出描述

要求输出一个整数,表示最后剩下的人的编号。

输入输出样例

输入:10 3

输出:4

运行限制
最大运行时间:1s
最大运行内存: 256M

2. 思路分析:

① 一开始最容易想到的是模拟的方法,编程模拟整个过程进行解决,只要是人数超过2个人那么就执行循环,在循环中使用一个变量进行计数,模拟报数的过程,但是由于数据量是比较大的,所以当数据量一大的时候那么就会超时,所以不能够使用模拟的方法进行解决。其实约瑟夫环是一个经典的数学问题,原始的描述为: N个人编号为1,2,……,N,依次报数,每报到M时,杀掉那个人,求最后胜利者的编号。约瑟夫环问题可以使用数学推导的方法进行解决,由一步步的公式推导最终得到答案,在网上查找了一下资料,发现理解其中的思路之后还是可以解决的,下面举一个比较简单例子会更容易理解(使用了csdn博客中一位大佬的例子)

② 例子:一共有11个人,编号分别为1,2,3....11,他们先围成一排,假设每报到3的人被杀掉,问最终没有被杀掉的那个人的编号是多少?首先最简单的方法是先列出一张动态变化的表得出胜利者的编号,下面是推导过程的表,然后观察他们每一步胜利者编号与上一步的关系

假设存在一个函数f(n, k),函数表示的意思是目前有n个人报数,当报到k的时候胜利者的编号,根据上面的式子可以推导出这个公式:f(t, k) = (f(t - 1, k) + k) % t,其中t为目前有t个人报数。首先还是从第一次报数开始,编号为1~11,当叫到3的时候那么编号为3的这个人被杀掉,然后从编号为4的人从1开始报数....可以发现其实编号为4之后的人在第二次报数的时候是往前移动了3个位置的(编号为4的排在第一位了),对于后面的例子也是这样,一直到最后胜利者。所以当只有一个人的时候那么胜利者的编号为1(对应下标为0)这样我们就可以从最终下标为0的人开始往上一步推导,可以发现f(2, 3) = (f(1, 3) + 3) % 2(目前有两个人),其实也就是刚才的逆过程,往前推导(一开始胜利者的编号)那么就需要往后移动k个位置,所以需要加上3(之前往只有胜利者一个人的时候推导是减3),而目前有k个人,所以我的下标是不能够超过k的,所以需要对k取余表示胜利者在后移动k步之后再当前人数下的下标,....一直到有n个人的时候那么就结束了了。所以只要在循环中一步步推导即可。

总的来说就是往前移动还是往后移动的为问题,看上面的数字移动规律就可以理解

3. 代码如下:

if __name__ == '__main__':
    # 本质上是移动了k个位置, 所以我们可以从最后胜利的那个人的下标0开始往上推导
    n, k = map(int, input().split())
    index = 0
    for i in range(2, n + 1):
        # 对i取余是上一步的情况下往后移动k个位置在目前i个人的真实编号
        index = (index + k) % i
    # 因为编号是从1开始的所以要返回index + 1
    print(index + 1)

猜你喜欢

转载自blog.csdn.net/qq_39445165/article/details/115199150