数据结构与算法-链表(八):剑指offer-孩子们的游戏

孩子们的游戏

题目

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

如果没有小朋友,请返回-1

思路

先举一个简单的例子:对于0,1,2,3,4的问题,m = 2, 第一次删除的人编号为1,那么之后重新报号的人的编号为2,问题变为2,3,4,0,为了使得该子问题能够与父问题一致(报号都从0开始,这样就可以采用相同的逻辑处理问题,这是递归经常使用的),现在我们把他们的编号做一下转换:

2 –> 0

3 –> 1

4 –> 2

0 –> 3

这样子问题变为0,1,2,3中删除第m个人,因为问题与父问题一致,所以可以采用相同的逻辑继续求解,这样直到问题规模为1,递归结束,得到最后胜利者。但是我们要记住,子问题的结果都是父问题为了使得问题逻辑一样而改变了编号值,所以我们要向上不断恢复编号值!

这个问题其实是需要找规律的,并且将一个复杂的问题推导成一个简单的数学公式。现在我们假设有一个函数 f ( n , m ) f(n,m) ,这个函数代表的是有 n 1 n-1 个人,从 0 , 1 , 2... , n 1 0,1,2...,n-1 开始往后查,找到第 m m 个数删除,然后再往后找,最终剩下的数字。

在这 n n 个数字中,第一个被删除是 ( m 1 ) % n (m-1)\%n ,这个是取余,也叫做取mod, ( m 1 ) / n (m-1)/n 的余数,被删除的这个数字我们记为k,那么k之后剩下的n-1个数字为 0 , 1 , 2 , . . . , k 1 , k + 1 , . . . , n 1 0,1,2,...,k-1,k+1,...,n-1 ,并且下一次开始从 k + 1 k+1 计数。相当于在剩下的序列中 k + 1 k+1 排在最前面,从而形成 k + 1 , . . . , n 1 , 0 , 1 , . . . , k 1 k+1,...,n-1,0,1,...,k-1 .由于 n 1 n-1 这个序列的规律并不是从0开始计数,所以我们新定义一个函数 f ( n 1 , m ) f'(n-1,m) .如果我们能够找到这两个函数之间的关系,那我们就可以利用程序很简单的进行循环得到最终结果。

不难看出,无论是从n项找到最后的数字还是从n-1项(是原问题的子集)找到最后的数字,结果应该是一样的,因此有 f ( n , m ) = f ( n 1 , m ) f(n,m) = f'(n-1,m) .接下来我们将通过一个简单的映射,将两个不同的函数组成的方程变为一个。

我们将这n-1组成的数字做成一个映射如下:
在这里插入图片描述
我们将映射定义为 p ( x ) = ( x k 1 ) % n p(x) = (x-k-1)\%n ,比如 x = k + 1 x = k+1 时,他的映射为0,依次类推(注意 2 % n = n 2 -2\%n = n-2 ).该映射的逆映射为 p 1 ( x ) = ( x + k + 1 ) % n p^{-1}(x)=(x+k+1)\%n 。由于映射之后的序列与映射之前的序列具有同样的形式,都是从0开始,所以有: f ( n 1 , m ) = p 1 ( f ( n 1 , m ) ) = [ f ( n 1 , m ) + k + 1 ] % n . f'(n-1,m)=p^{-1}(f(n-1,m))=[f(n-1,m)+k+1]\%n.

所以现在就会有了一个关于f的递推公式。考虑边界条件,当n=1时,f返回的就是0,因此递推公式如下: f ( n , m ) = { 0 n = 1 [ f ( n 1 , m ) + m ] % n n > 1 f(n, m)=\left\{\begin{array}{ll} 0 & n=1 \\ [f(n-1, m)+m] \% n & n>1 \end{array}\right.

之后就可以编程实现了!

# -*- coding:utf-8 -*-
class Solution:
    def LastRemaining_Solution(self, n, m):
        # write code here
        # f(n)=[f(n-1)+m]%n
        if n == 1:
            return 0
        if n == 0 or m == 0:
            return -1
        value = 0  # f(1)=0
        for i in range(2, n+1):
            ret = (value + m)% i
            value = ret
        return ret
发布了49 篇原创文章 · 获赞 2 · 访问量 1821

猜你喜欢

转载自blog.csdn.net/liuluTL/article/details/105011031