1. 程式人生 > >約瑟夫環之二(用遞迴的思想解決Josephus問題)【轉】

約瑟夫環之二(用遞迴的思想解決Josephus問題)【轉】

初始情況: 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.遞迴:

  1. #include <stdio.h>

  2. int josephus(int n, int m) {

  3. if(n == 1) {

  4. return 0;

  5. }

  6. else {

  7. return (josephus(n-1, m) + m) % n;

  8. }

  9. }

  10. int main() {

  11. int n, m;

  12. while (scanf("%d", &n) == 1) {

  13. if (!n) {

  14. break;

  15. }

  16. scanf("%d", &m);

  17. int result = josephus(n, m);

  18. printf("%d\n", result+1);

  19. }

  20. return 0;

  21. }

2.迭代:

  1. #include <stdio.h>

  2. int main() {

  3. int n, m, i, result;

  4. while (scanf("%d", &n) == 1) {

  5. if (!n) {

  6. break;

  7. }

  8. scanf("%d", &m);

  9. result = 0;

  10. for (i = 2; i <= n; i++) {

  11. result = (result + m) % i;

  12. }

  13. printf("%d\n", result + 1);

  14. }

  15. return 0;

  16. }

--------------------- 本文來自 d4shman 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/wusuopubupt/article/details/18214999?utm_source=copy