美文网首页
45、圆圈中最后剩下的数字

45、圆圈中最后剩下的数字

作者: 小碧小琳 | 来源:发表于2018-10-06 14:49 被阅读0次

    经典的约瑟夫环问题。

    • 当队列长度为1时,结束while循环,返回结果。
    • 每次走m步,其中每一步都要把队首的元素添加到队尾。走到第m步弹出元素。
    • 弹出哪个元素,需要举例子好好看一看。
      代码实现:
    # -*- coding:utf-8 -*-
    class Solution:
        def LastRemaining_Solution(self, n, m):
            if n == 0:
                return -1
            # 构造一个先进先出的队列
            Que = [x for x in range(n)]
            Que.reverse()
    
            while len(Que) > 1:
                for i in range(m):
                    Que.insert(0, Que.pop())
                Que.pop(0)
    
            return Que[0]
    

    创新的利用数学求解的解法,参考剑指offer的解:

    首先,对于从0到n-1的数,我们定义一个函数f(n,m) = last,last表示按照规则每次删除第m个数以后,留下的最后的数字。

    我们把剩下的n-1个数字序列做个映射,形成从0到n-2 的序列。这样,对于这个从0到n-2的序列,同样可以用上述的函数f(n-1,m)来表示此时最后一个数是多少。(注意,不同于f(n,m),二者结果不一定一样的)

    这样,由上面知道,f(n,m) = f''(n-1,m),而f''(n-1,m)又可以通过映射关系,用f(n-1,m)。因此,我们终于找到了f(n,m)与f(n-1,m)之间的计算关系。(相当于状态转移方程),那么用递归或者循环,就都好做了。

    如下图,对于左侧所有的元素x,都可以通过P映射到右边。

    此时, f''(n-1,m)就等于左侧的其中一个值,而f(n-1,m)等于右侧的其中一个值。从左侧映射到右侧,通过P来映射。
    但是我们想达到的目的是,想用f(n-1,m)来表示 f''(n-1,m),这样可以与f(n,m)联系起来。于是,我们想到了逆映射。

    对于其中对n求余数,映射与逆映射都是对n求余数,以及把k带入以后对n的求余消去,需要注意一下。

    对于上图,左侧是x,右侧是P(x)。

    我们想用f(n-1,m)来表示 f''(n-1,m),于是有逆映射来表示。于是:

    最终得到了,函数f(n,m)与f(n-1,m)之间的关系。

    注意,这里的f(n,m)其中n等于几,i最终就要到几停止循环。所以要用range(2,n+1)来保证i最终取n。

    于是可以通过循环实现代码:

    # -*- coding:utf-8 -*-
    class Solution:
        def LastRemaining_Solution(self, n, m):
            if n < 1 or m < 1:
                return -1
    
            #last初始化为0,n=1时,last也就是0
            last = 0
            for i in range(2,n+1):
                last = (last + m) % i
            return last
    
    S = Solution()
    print(S.LastRemaining_Solution(5,3))
    

    相关文章

      网友评论

          本文标题:45、圆圈中最后剩下的数字

          本文链接:https://www.haomeiwen.com/subject/npzcaftx.html