孩子们的游戏(圆圈中最后剩下的数)

来源:互联网 发布:windows播放器下载 编辑:程序博客网 时间:2024/05/27 14:14

题目描述

首先,让小朋友们围成一个大圈。然后随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数….这样下去….直到剩下最后一个小朋友获得神秘大奖,。请你试着想下,哪个小朋友会得到这份神秘大奖呢呢?(注:小朋友的编号是从0到n-1)

题解

解法1

模拟环形链表
这里通过取模运算直接算出要删除的位置,简化了寻找的过程

class Solution {public:    int LastRemaining_Solution(int n, int m)    {        if(n == 0)  return -1;        //初始化小朋友们        vector<int> haha;        for(int i = 0; i < n; i++){            haha.push_back(i);        }        //进行游戏        int i = 0;  //上次删除的位置        while(haha.size() > 1){            i = (i + m - 1) % haha.size();            haha.erase(haha.begin() + i);        }        return *haha.begin();    }};

解法2

定义f(n, m) 表示每次在0到i-1中删除第m个数最后剩下的数。

第一次编号为 k = (m-1)%n 的小朋友出列之后,剩下的n-1个人组成了一个新的约瑟夫环:

k k+1 k+2 … n-2, n-1, 0, 1, 2, … k-2并且从k开始报0。

由于这个序列的规律与之前的不一样,记为f’(n-1,m),值与f(n,m)相同。
现在我们把他们的编号做一下转换:

k+1   --> 0k+2   --> 1......k-2   --> n-3k-1   --> n-2

变换后成为了 n-1 个人报数的子问题,其结果为f(n-1,m), 根据映射可求出
f’(n-1,m) = ( f(n-1,m) + k + 1) % n
代入 k = (m-1) % n,
f(n,m) = f’(n-1, m) = ( f(n-1,m) + m) % n。

最终得到递归公式为

f(1, m) = 0;  //终止条件f(i, m) = (f(i-1,m) + m) % i;  //i>1

代码可选择递归或循环

class Solution {public:    int LastRemaining_Solution(int n, int m)    {        if(n == 0)  return -1;        int res = 0;        for(int i = 2; i <= n; i++){            res = (res + m) % i;        }        return res;    }};
0 0