Josephus Problem

问题描述:
n个罪犯被一群人追杀,为了不被敌人杀死,n个罪犯决定通过如下的方式自杀:手拉手围成一圈,并对每个人按顺序编号为:1,2,3,...,n。
从1开始报数,报到k的那个人就自杀,再从自杀的下一个人继续从1开始报数,以此类推,我们能够知道最后幸存的那个人的编号吗?

解决方法:
通过DP能够在$O(n)$解决问题。
首先我们需要递推式:

设$f(n,k)$表示有n个人且报到k的人自杀的情况下,最后幸存的人的编号。


\[f(n,k)=(f(n-1,k)+k - 1)\%n + 1\]

其中 $f(1,k) = 1$。

如果有 $n$ 个人且报到 $k$ 的人自杀时,第一个自杀的人一定是编号为 $(k-1) \% n + 1$ 的人,因此剩下还有$n-1$ 个人,把 $f(n,k)$ 想象成一个子程序,你能够调用它,我们只需要把剩下的 $n-1$ 个人重新编号,就能用 $f(n-1,k)$ 解决了。重新编号如下:

原来的编号 1    2    ...  k-1  k+1    k+2 ...  n
现在的编号 n-k+1  n-k+2         n-1   1      2     n-k

假设现在编号为 $i$,则原来的编号为$(i+k-1) \% n + 1$(注意,这里不是(i+k) mod n,因为当i=n-k时,(i+k) mod n = 0)。

如果按照“现在的编号”的方式,用 $f(n-1,k)$ 能够找到幸存者,因此再将“现在的编号”转换成原来的编号即可,即 $f(n,k)=(f(n-1,k)+k - 1)\%n+1$

另解:如果按照 0,1,2,...,n-1 编号,则递推式变成了:

\[f(n,k)=(f(n-1,k)+k)\%n\]

其中$f(1,k)=0$

代码实现:

 1 public class Josephus {
 2     public static int josephus0(int n,int k){
 3         int r = 0;
 4         for(int i=2;i<=n;i++){
 5             r = (r + k) % i;
 6         }
 7         return r;
 8     }
 9     public static int josephus1(int n,int k){
10         int r = 1;
11         for(int i=2;i<=n;i++){
12             r = (r + k - 1) % i + 1;
13         }
14         return r;
15     }
16     public static void main(String[] args) {
17         System.out.println(josephus1(10000,2));
18     }
19 }
Josephus的Java实现

复杂度:$O(n)$

作者:xiazdong
出处:http://blog.xiazdong.info
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
原文地址:https://www.cnblogs.com/xiazdong/p/3104117.html