約瑟夫環之二(用遞迴的思想解決Josephus問題)
阿新 • • 發佈:2019-01-31
初始情況: 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
現在我們把他們的編號做一下轉換:
x' -> x
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
那麼,如何知道(n-1)個人報數的問題的解?只要知道(n-2)個人的解就行了。(n-2)個人的解呢?只要知道(n-3)的情況就可以了 ---- 這顯然就是一個遞迴問題:
令f[i]表示i個人玩遊戲報m退出最後勝利者的編號,最後的結果就是f[n]
遞推公式
f[1]=0;
f[i]=(f[i-1]+k)%i = (f[i-1] +m%i) % i = (f[i-1] + m) % i ; (i>1)
1.遞迴:
#include <stdio.h> int josephus(int n, int m) { if(n == 1) { return 0; } else { return (josephus(n-1, m) + m) % n; } } int main() { int n, m; while (scanf("%d", &n) == 1) { if (!n) { break; } scanf("%d", &m); int result = josephus(n, m); printf("%d\n", result+1); } return 0; }
2.迭代:
#include <stdio.h>
int main() {
int n, m, i, result;
while (scanf("%d", &n) == 1) {
if (!n) {
break;
}
scanf("%d", &m);
result = 0;
for (i = 2; i <= n; i++) {
result = (result + m) % i;
}
printf("%d\n", result + 1);
}
return 0;
}