剑指Offer——孩子们的游戏(圆圈中最后剩下的数)
题目描述:
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
分析:
这是约瑟夫环(约瑟夫问题)。
问题是从n个人编号分别为0...n-1,取第m个,
则第m个人编号为m-1的淘汰,剩下的编号为0,1,2,3...m-2,m,m+1,m+2,...
此时因为从刚刚淘汰那个人的下一个开始数起,因此重新编号
把m号设置为0,则
m 0
m+1 1
...
0 n-m
1 n-m+1
假设已经求得了n-1个人情况下的最终胜利者保存在f[n-1]中,
则毫无疑问,该胜利者还原到原来的真正编号即为 (f[n-1]+m)%n。
(因为第二轮重新编号的时候,相当于把每个人的编号都减了m,因此重新+m即可恢复到原来编号)。
由此,我们可以想象,当最终只剩下一个人的时候,该人即为胜利者,此时重新编号,因为只有一个人,
所以此时f[1]=0。
这样f[2]=(f[1]+m)%2,这样就可以求出最终胜利者在2个人的时候的情况下的编号,
由递推公式f[n]=(f[n-1]+m)%n,可递推到最初编号序列中该胜利者的编号。
代码:
1 class Solution { 2 public: 3 int LastRemaining_Solution(int n, int m) { 4 if(n == 0 || m == 0) return -1; 5 int res = 0; 6 for(int i = 2; i <= n; i++) res = (res + m) % i; 7 return res; 8 } 9 };