1题目描述:
每年六一儿童节,张东升都会准备一些小礼物去看望孤儿院的普普和严良,今年亦是如此。张朝阳作为张东升的资深摄影发烧友,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且获得张东升名贵的“爬山一日游”不归版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)。如果没有小朋友,请返回-1
2.解题思路
f(n,m)
:表示每次在n个数字0,1…n-1中删除第m个数字最后剩下的数字。
在这n个数字中,第一个被删除的数字是(m- 1)%n
(取余的原因是m可能比n大),记作k,则k=(m-1)%n
。删除后的序列为0,1,…k-1,k+1,…,n-1。由于下一次删除是从k+ 1开始计数的,所以相当于从标号为k+ 1,k+2, …,n-1,0,1,2…k-1的序列中继续删除第m个数字,最终剩下的数字就是结果。
剩下的n-1个数字如果重新按顺序标号得到序列0,1,…,n-2.则每次删除第m个数,最后剩下的数字就是f(n-1,m)。由于重新标号了,所以并不是f(n, m)=f(n-1,m)。事实上,原先的不连续的序列A(k+ 1,k+2,…,n-1,0,1,2,…k-1)变成了序列B(0,1,…,n-2),而我们主要是想知道如何从序列B中的某个数找到序列A中对应的关系,先建立个映射表格:
根据表格,可以很直观的看出,在B序列中数字x
,对应于A序列中的(x+k+1)%n
(注意:必须取余数,因为B序列中为n-k
,而n- k+k+1
为n+1
,必须取余数才能得到1
)。所以在B序列中标号为f(n-1,m)
,对于在A序列中就为(f(n-1,m)+k+1)%n
。还记得k吗?k就是在第一次删除的时候删掉的数(与n有关的变量), k=(m- 1)%n
。代入上面的式子中,得到:(f(n-1,m)+k+ 1)%n = (f(n-1,m)+(m-1)+ 1)%n = (f(n-1,m)+ m)%n
以上的推导过程记不住没关系,只要记住下面这个核心公式:
3.编程实现(Java):
//递归
public class LastRemaining_Solution_29 {
public int LastRemaining_Solution(int n, int m) {
if (n < 1 || m < 1)
return -1;
if (n == 1)
return 0;
return (LastRemaining_Solution(n - 1, m) + m) % n;
}
}
//迭代
public int LastRemaining_Solution(int n, int m) {
if (n < 1 || m < 1)
return -1;
if (n == 1)
return 0;
int last = 0;
for (int i = 2; i <= n; i++) {
last = (last + m) % i;
}
return last;
}