孩子们的游戏
题目描述
每年六一儿童节,牛客都会准备一些小礼物和小游戏去看望孤儿院的孩子们。其中,有个游戏是这样的:首先,让 n 个小朋友们围成一个大圈,小朋友们的编号是0~n-1。然后,随机指定一个数 m ,让编号为0的小朋友开始报数。每次喊到 m-1 的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0… m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客礼品,请你试着想下,哪个小朋友会得到这份礼品呢?
-
暴力模拟
可以使用环形队列,模拟题目要求,每当到m - 1个个孩子的时候就从队列中删除,依次循环,直到剩下一个孩子。
使用数组模拟,但是需要注意取模操作,本质也是模拟环形队列,暴力代码不太好写,所以比较推荐第二种方法,也就本篇博客主讲的方法,动态规划。 -
动态规划
状态表示是到那个有i个孩子围成一圈的时候,最后剩下的那个孩子的编号是什么。
所以现在假设有i个孩子维成一圈。
动态规划本质就是寻找dp[i]和dp[i - 1] 的关系,所以我们先找到m-1的孩子把它移除。
现在假设m-1就落在了4这个位置,那么我们可以把5这个孩子当做0下标,然后它的结果就存在dp[i - 1]中。
但是5并不是0下标,所以我们需要将m下标与0下标进行一个映射也就是,假设当前的下标是pos, 也就是需要从红色的下标对应到黑色的下标处。那么对应关系就是pos +m。但是对于i - 1下标来说,如果+m就越界了,所以需要对当前的孩子个数取个%,这样就能保证不越界。
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型
* @param m int整型
* @return int整型
*/
int LastRemaining_Solution(int n, int m)
{
// write code here
int res = 0;
for(int i = 1; i <= n; ++i)
{
res = (res + m) % i;
}
return res;
}
};