本题来自NowCoder网《剑指offer》:
每年六一儿童节,NowCoder都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为NowCoder的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数….这样下去….直到剩下最后一个小朋友,可以不用表演,并且拿到NowCoder名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?
最开始也是没头绪,有想到用递归的思路去解决,但是它传入的参数是一个int n ,int m,n代表人数,m代表报数。递归不能去破坏人员的编号,这里是比较难处理的问题。最终我构造了一个新的方法用一个数组去维护,将编号定义为Integer的数据,这样用集合去递归,而不是用int n去递归。
public int LastRemaining_Solution(int n, int m) {
if(n == 0) return -1;
if(n == 1) return 0;
else {
// 创造一个数组,去初始化这个list
List list = new ArrayList<Integer>();
for(int i = 0;i < n;i ++){
list.add(i);
}
// 调用新的方法
return getRemaining(list, m);
}
}
private int getRemaining(List<Integer> list,int m) {
// 递归出口,剩下一个元素,返回该元素
if(list.size() == 1) return list.get(0);
// 获取出队的人员数组下表
int index = m % list.size() == 0 ? list.size() - 1:(m % list.size() - 1);
// 新建一个list用于递归
List<Integer> tempList = new ArrayList<Integer>();
for(int i = index + 1;i < list.size();i ++) {
tempList.add(list.get(i));
}
for(int i = 0;i < index;i ++){
tempList.add(list.get(i));
}
return getRemaining(tempList, m);
}
后来看了别人讨论,这是一个约舍夫问题,用很简单的递归代码去实现了,主要是找到递归的规律。
int LastRemaining_Solution(unsigned int n, unsigned int m)
{
if(n==0)
return -1;
if(n==1)
return 0;
else
return (LastRemaining_Solution(n-1,m)+m)%n;
}