約瑟夫環的兩種解法

2021-08-28 17:00:26 字數 1801 閱讀 6253

n個人想玩殘酷的死亡遊戲,遊戲規則如下:

n個人進行編號,分別從1到n,排成乙個圈,順時針從1開始數到m,數到m的人被殺,剩下的人繼續遊戲,活到最後的乙個人是勝利者。

請輸出最後乙個人的編號。

input

輸入n和m值。m>1。

output

輸出勝利者的編號。

sample input

5 3sample output

4hint

第一輪:3被殺第二輪:1被殺第三輪:5被殺第四輪:2被殺

#include #include using namespace std;   //使用命名空間std

//鍊錶法

struct lnode

;typedef struct

dlinklist;

int main()

kill.befo->pre = kill.rear;

//約瑟夫環初始化完成

int flag = 1; //計數器

lnode* t, *j; //工作指標

t = kill.befo;

while (flag < n)

cout << t->no << endl;

delete t;

} return 0;

}

(2)用陣列模擬迴圈鍊錶

#include #include using namespace std;   //使用命名空間std

int main()

cout << i+1 << endl; //邏輯次序從0開始,實際編號從1開始

} return 0;

}

第乙個人(編號一定是(m-1)%n,設之為(k-1) ,讀者可以分m=n的情況分別試下,就可以得出結論) 出列之後,

剩下的n-1個人組成了乙個新的約瑟夫環(以編號為k==m%n的人開始):

k k+1 k+2 … n-2, n-1, 0, 1, 2, …,k-3, k-2

現在我們把他們的編號做一下轉換:

old -> new

k --> 0

k+1 --> 1

k+2 --> 2……

k-2 --> n-2

k-1 --> n-1

變換後就完完全全成為了(n-1)個人報數的子問題,假如我們知道這個子問題的解:例如new是最終的勝利者,那麼根據上面這個表把這個new變回去不剛好就是n個人情況的解嗎!

new ->old?(這正是從n-1時的結果反過來推n個人時的編號!)

0 -> k

1 -> k+1

2 -> k+2……

n-2 -> k-2

變回去的公式 old=(new+k)%n

那麼,如何知道(n-1)個人報數的問題的解?只要知道(n-2)個人的解就行了。(n-2)個人的解呢?只要知道(n-3)的情況就可以了 ---- 這顯然就是乙個遞迴問題:

令f[i]表示i個人玩遊戲報m退出最後勝利者的編號,最後的結果就是f[n]

遞推公式

f[1]=0;

f[i] = (f[i-1] + m) % i ; (i>1)

(1)遞迴法(反向推)

#include #include using namespace std;   //使用命名空間std

//遞推法

int main()

return 0;

}int f(int n,int m)

return 0;

}

約瑟夫環問題的兩種解法(詳解)

題目 josephus有過的故事 39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死也不要被敵人抓。於是決定了自殺方式,41個人排成乙個圓圈,由第1個人開始報數,每報數到第3人該人就必須自殺。然後下乙個重新報數,直到所有人都自殺身亡為止。然而josephus 和他的朋友並...

約瑟夫環的兩種實現

方法一 用乙個陣列存放每個元素,再用乙個bool陣列存放每個元素是否還在那個佇列中。每次出列乙個元素,將其對應的那個bool值置為false。迴圈輸出第m個元素直到只剩下乙個元素在佇列中。不過這個時間複雜度比較高。includeusing namespace std 時間複雜度高的方法 define...

約瑟夫環O N 解法

無論是用鍊錶實現還是用陣列實現都有乙個共同點 要模擬整個遊戲過程,不僅程式寫起來比較煩,而且時間複雜度高達o nm 當n,m非常大 例如上百萬,上千萬 的時候,幾乎是沒有辦法在短時間內出結果的。我們注意到原問題僅僅是要求出最後的勝利者的序號,而不是要讀者模擬整個過程。因此如果要追求效率,就要打破常規...