用C 解決約瑟夫環的問題

2021-10-04 02:42:42 字數 2400 閱讀 9596

猶太歷史學家弗拉维奧·約瑟夫在他的日記中提到過這麼乙個問題:他和他的40個戰友被羅馬軍隊包圍在洞中。它們討論是自殺還是投降,最終決定採用抽籤的方式決定自殺的順序。演算法是這樣的:所有人站成一圈,依次報數從1到3,每報到3的則自殺,下乙個再從1開始報數。這樣,所有人會依次自殺,直到最後乙個。那最後乙個死不死又有誰知道呢?那麼站的位置就顯得很重要了,到底站在哪個位置才能最後乙個死呢?這便衍生成了乙個計算機中的演算法問題。

為了更加突出的說明這一問題,請看下面的圖,改圖內圈數字表示站立下標,外圈數字表示死亡順序。

可見當有10個人,報數為3的人死亡時,站在4號位的最後可以存活。這一問題可以模擬到電腦程式中。

解決思路:首先很容易想到的便是採用鍊錶的方式來處理,有乙個首位相連的鍊錶,每個節點裡面儲存了自己的下標並指向下乙個節點的位址,最後乙個節點指向首節點。根據這一思路,我們可以先建立乙個約瑟夫環如下**:

struct jospher};

//先定義乙個結構體(類)儲存每個節點的資訊

void

creatring

(jospher* head,

int ringsize)

pointer-

>next = head;

//最後將首節點放入末尾構成環

}

以上**成功的構建了乙個約瑟夫環,現在將模擬死亡順序,我們需要兩個可移動的指標,用來依次殺掉報到3的人(刪掉對應的節點),可以參考下一幅圖:

我們知道,報數是1,2,3,1,2,3…依次迴圈的,因此第一輪的報數是直接從1開始加,如圖中的步驟1號指標(紅色)指向首節點,2號指標(藍色)指向其下乙個節點,1報完之後是2,2正常,因此,將藍色指標向後移動一位,紅色指標也向後移動一位。再到3號報數,同樣地,先將藍色指標向後移動一位(此時指向4),再將此時的藍色指標存入紅色指標的next裡面,再刪掉3節點,那麼第乙個刪除就完成了。依此類推可以逐個刪掉直至最後一名存活者。值得注意地是,當只剩最後乙個存活者時,它的next指向的是它本身。因此用**實現如下:

void

killnode

(jospher* head,

int killsize)

else

} cout <<

"the victor is: " first-

>index -1;

//迴圈結束後剩下的節點即為存活的節點,下標要減1

}

最後,稍微測試一下這個鍊錶:

int

main()

輸出結果:

最後是下標為3即站在4號位的人存活。

以上便是採用鍊錶解決這個問題的方法,以上**的時間複雜度為o(mn),即n個人,死亡號碼為m,當n足夠大時便顯現出了**的不足。因此我們還可以採用公式法來遞推。觀察以下**:

從上表可以看出4號位也就是下標為3的人存活。公式可以歸納如下:

f(n) = (f(n - 1) + m) % n,該公式表示,當剩餘n(假設為10)個人時,此場景的存活下標為剩餘n-1(即9)人時的存活下標加上報數迴圈長度(3)除以當前人數n(即10)的餘。很明顯,這是個遞迴公式,可以很簡單的用程式寫出來。問題是,這個公式是怎麼來的?

可以想象,當已知n為10時,存活的下標肯定為3。當殺掉乙個人之後(即剩9個人)這時,存活下標一定是當前下標往前推3(乙個迴圈),即為0(不信可以用鍊錶**驗證以下)。再死乙個人時,再往前推3(即8, 7, 6),下標為6,依次類推。直至只剩1個人時的下標一定是0.因此可以用遞迴法反向推出。**如下:

int

jospherring

(int ringsize,

int killindex)

輸出結果與鍊錶法一致。

由此可見公式遞迴法比鍊錶法來的要簡單的多。但是它仍然有乙個弊端,那就是該程式無法知道死亡的次序,而鍊錶法則可以得出依次死亡的次序(只需在delete要刪除的節點之前將其中的index減1即可)。如下面輸出結果:

以上兩種方法各有各的使用場景,選擇最合適的即可!

c 解決約瑟夫環問題

c 解決約瑟夫環問題 約瑟夫 josephus 問題 m個人圍坐成一圈,從1開始順序編號 遊戲開始,從第乙個人開始由1到n迴圈報數 報到m的人退出圈外問 最後留下的那個人原來的序號 本題可以定義乙個容器 vector 初始化大小 元素個數 為n。容器裡元素的值標識該人是否出局,1在圈內,0出局。值為...

約瑟夫環問題的解決

約瑟夫環問題介紹 已知n個人 以編號1,2,3.n分別表示 圍坐在一張圓桌周圍。從編號為1的人開始報數,數到m的那個人出列 他的下乙個人又從1開始報數,數到m的那個人又出列 依此規律重複下去,直到圓桌周圍的人全部出列。include include include typedef int datat...

約瑟夫環問題的解決

約瑟夫環問題介紹 已知n個人 以編號1,2,3.n分別表示 圍坐在一張圓桌周圍。從編號為1的人開始報數,數到m的那個人出列 他的下乙個人又從1開始報數,數到m的那個人又出列 依此規律重複下去,直到圓桌周圍的人全部出列。include include include typedef int datat...