孩子们的游戏
题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
如果没有小朋友,请返回-1
分析推理
举例:
m=4,n=9,第k轮挑选的索引以及新的变化依次如下:
0: 0,1,2,3 ,4,5,6,7,8 (0,1,2)
1: 0,1,2,3 ,4,5,6,7(0,1,2)
2: 0,1,2,3 ,4,5,6
…
…
7: 0,1(0,1 )
8: 此时只剩一个,即最后一个当前索引一定是0 。
推导
- 首先第一次(k=0)筛选的是m-1=3;
- 第二次(k=1),找到索引 i1 和i0 的关系(需要是 i1 推到出 i0-1,因为最终要知道初始索引 ): i0= (i1+4)% 9,当 i1=m-1=3时, i0=(3+4)%9=7;
- 第三次(k=2),推导出 i2 和i0 的关系: i1= (i2+4)% 8,当 i2=m-1=3时, i1=(3+4)%8=7, i0=(7+4)%9=2;
- 第k次和第k-1次索引的关系: ik-1=( i1+m)%(n-(k-1))(0<k<n);
- 从已知最后一次剩余的当前索引是0,依次倒推回第一轮中的此0索引对应的初始所引,即为剩余的最后一个孩子的编号。
时间缓缓地流失,再来看题的时候总有新的收获
推导公式的本质其实就是:因为取的是第m个,从他后边重新排,相当与编号永远都是右移m;其次,因为每次人数会少一个所以取余的分母每次减1 。原来这么简单?曾经我怎么就绕了那么大一个丸子?
代码
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if(n == 0 || n == 1)
return n-1; // 一个小朋友那么就是他(0):1 --> 0,0 --> -1
int index = 0;
for(int k = n-1;k > 0;k--){
index = (index+m)%(n-(k-1));
}
return index;
}
}
我这里推导有点倒着来了,k可以从2开始,道理一样:
for(int k = 2;k < n;k++){
index = (index+m)%k;
}