问题描述:n个人 ( 编号0~(n-1) ),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
最直白的方法就是用链表去模拟整个过程就好了,但是这个的复杂度有点高,算不上一个非常优秀的做法。
下面进行推导,看是否能够推出一个通用的公式这样就可以直接得出答案:
初始情况: 0, 1, 2 ......n-2, n-1 (共n个人)
第一个人(编号一定是(m-1)%n,设之为(k-1) ,读者可以分m<n和m>=n的情况分别试下,就可以得出结论) 出列之后,
剩下的n-1个人组成了一个新的约瑟夫环(以编号为k==m%n的人开始):
k k+1 k+2 ... n-2, n-1, 0, 1, 2, ...,k-3, k-2
现在我们把他们的编号做一下转换:
k --> 0
k+1 --> 1
k+2 --> 2
...
...
k-2 --> n-2
k-1 --> n-1
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗!
x ->x' (这正是从n-1时的结果反过来推n个人时的编号!)
0 -> k
1 -> k+1
2 -> k+2
...
...
n-2 -> k-2
变回去的公式 x'=(x+k)%n
递推公式
f [1] = 0;
f [ i ] = ( f [i -1] + m) % i; (i>1)
递归写法:
int josephus(int n,int m) { if (n == 1) return 0; else return (josephus(n-1,m) + m) % n; }
迭代写法:
int f[maxn]; int josephus(int n,int m) { if (n < 1 || m < 1) return -1; for (int i = 2;i <= n;i++) f[i] = (f[i-1] + m) % i; }
来源:https://www.cnblogs.com/-Ackerman/p/12169034.html