题目描述
首先,让小朋友们围成一个大圈。然后随机指定一个数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 --> 0
k+2 --> 1
...
...
k-2 --> n-3
k-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;
}
};