環形鍊錶的約瑟夫問題(高階)
據說著名猶太歷史學家 josephus 有過以下故事:在羅馬人占領喬塔帕特後,39 個猶太人與 josephus 及他的朋友躲到乙個洞中,39 個猶太人決定寧願死也不要被敵人抓到,於是決定了一種自殺方式,41 個人排成乙個圓圈,由第 1 個人開始報數,報數到 3 的人就自殺,然後再由下乙個人重新報 1,報數到 3 的人再自殺,這樣依次下去,直到剩下最後乙個人時,那個人可以自由選擇自己的命運。這就是著名的約瑟夫問題。現在請用單向環形鍊錶得出最終存活的人的編號。
輸入描述:
一行兩個整數 n,m,n 表示鍊錶的長度,m 表示每報數到 m 就自殺。
輸出描述:
輸出最後存活的人的編號(編號從 1 開始到 n)。
示例1輸入
5 2
輸出3
備註:
1 ≤n
,m≤5
∗106
1 \leq n,m \leq 5*10^6
1≤n,m≤
5∗10
6題解:
觀察一下資料,在前面一篇 環形鍊錶的約瑟夫問題 中,資料範圍較小,可以以 o(n
∗m
)o(n*m)
o(n∗m)
的時間複雜度進行模擬,但是本題的資料範圍意味著我們需要考慮 o(n
)o(n)
o(n)
時間複雜度的演算法。之前的模擬是因為我們不知道到底哪個會活下來,所以通過不停的刪除來淘汰無關節點,直到剩下乙個節點。如果我們可以不用一直刪除的方式,而是直接通過 n 和 m 計算哪個節點會活下來,然後找到該節點的編號即可。那麼怎麼找到呢?考慮如下問題:
1個人:fun(1) = 1;
2個人:fun(2);
n-1個人:fun(n-1);
n個人:fun(n)。
我們已經知道 fun(1) = 1 ,如果能確定 fun(i-1) 和 fun(i) 之間的關係,就可以通過遞迴求出 fun(n) 了。
假設 n 和 m 分別為 5 和 3,假設編號為 a 的人報的是 b ,則 a 和 b 的對應關係如下:ab
1122
3341
52上面假設了 n 和 m 分別是 3 和 5 ,第乙個被殺掉人的編號為 3 ,在殺掉 3 後,對剩下的人重新編號,如下:
a_old
a_new13
243x
4152
x 表示人已經被殺掉,從上表中可以看到,編號之間的關係為:old = (new + 3 - 1) % 5 + 1,改寫一下公式就是:old = (new + m - 1) % i + 1,i 表示有 i 個人。至此,我們找到了 fun(i-1)->new 和 fun(i)->old 之間的關係。
那麼剩下的就是寫**了,由於每個狀態只和前乙個狀態有關,可以通過迴圈實現,不需要用遞迴,見**:
**:
#include
using
namespace std;
intmain
(void
)
環形鍊錶 約瑟夫問題
問題描述josephu問題 設編號為1,2,3 n的n個人圍坐成一圈,約定編號為k的人從1開始報數,數到m的那個人出列,他的下一位從1開始報數,數到m那個人又出列,直到所有人都出列為止,由此產生乙個出佇列編號的序號。解決方法 建立乙個輔助指標helper,指向頭指標的前乙個節點 當小孩報數的時候,f...
Java環形鍊錶 約瑟夫問題
n個小孩圍成圈,丟手帕,從第start個小孩開始丟,每到第step個小孩出局 接著從下乙個小孩開始,直到最後乙個小孩為止,遊戲結束。author jiaozl cyclink cyclink new cyclink cyclink.setlen 5 cyclink.createlink cyclin...
約瑟夫問題 單向環形鍊錶
約瑟夫問題的示意圖 josephu 問題 josephu 問題為 設編號為 1,2,n 的 n 個人圍坐一圈,約定編號為 k 1 k n 的人從 1 開始報數,數到 m 的那個人出列,它的下一位又從 1 開始報數,數到 m 的那個人又出列,依次類推,直到所有人出列為止,由此 產生乙個出隊編號的序列。...