描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)。如果没有小朋友,请返回-1。
示例1
输入:5,3
返回值:3
方法一:递归求解
核心思想:
本题是一个比较经典的约瑟夫环问题,多个人围成一圈,然后给定一个数字和起始位置,从起始位置开始数,数到给定数字,则该位置的人出局,游戏继续从出局的下一个人开始,直到最后一个人为止游戏结束。
假设f(N,M)表示N个人报数,每次报数到M时,则当前报数人出局;
则f(N-1,M)表示N-1个人报数,每次报数到M时,则当前报数人出局。
f(N,M)=(f(N-1,M)+k)%N=(f(N-1,M) +M%N)%N=(f(N-1,M)+M)%N (N>1,k=M%N)
可以得出:f(N,M) = (f(N-1,M)+M)%N
核心代码:
int f(int n,int m){ if(n == 1) return 0; else return (f(n-1,m) + m) % n; } int LastRemaining_Solution(int n, int m) { if(n<1|| m<1) return -1; return f(n,m); }
复杂度分析:
递归n次,因此时间复杂度为O(N)。而每次递归都需要一个额外的变量保存,因此空间复杂度为O(N)
时间复杂度:O(N)
空间复杂度:O(N)
方法二:约瑟夫环递推公式
核心思想:
本题是一个比较经典的约瑟夫环问题,多个人围成一圈,然后给定一个数字和起始位置,从起始位置开始数,数到给定数字,则该位置的人出局,游戏继续从出局的下一个人开始,直到最后一个人为止游戏结束。
假设f(n)表示0~n-1共n个数字中最后剩下的数字,去掉的数字为(m-1)%n,而下一次报数是从m%n个数字开始(即出局的下一个为止),因此
f(n-1) 和 f(n)之前的关系如下:
f(n-1) f(n)
0 m%n
1 (m+1)%n
... ...
n-2 (m-2)%n
因此可以归纳总结:f(n) = (f(n-1)+m)%n (其中n是变化的,因为每次操作都会有人出局)
图解:
核心代码:
int LastRemaining_Solution(int n, int m) { if(n<1|| m<1) return -1; int last=0; for(int i=2;i<=n;i++) last=(last+m)%i; //这里的last相当于上面递推式中的f(n),i相当于n return last; }
复杂度分析:
代码使用了循环,循环次数为数组大小,因此该方法的时间复杂度为O(N)。由于没有采用了额外的数组空间,空间复杂度为O(1)
时间复杂度:O(N)
空间复杂度:O(1)