問題描述:
已知n個人(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號為1的人開始報數,數到m的那個人出列;他的下乙個人又從1開始報數,數到m的那個人又出列;依此規律重複下去,直到圓桌周圍的人全部出列,求最後乙個出列人的編號。
為了討論方便,先把問題稍微改變一下,並不影響原意:問題描述:n個人(編號0~(n-1)),從0開始報數,報到(m-1)的退出,剩下的人繼續從0開始報數。求最後乙個人的編號。
第乙個出列人的編號一定是(m-1)%n,他出列之後,剩下的n-1個人組成了乙個新的約瑟夫環(以編號為k=m%n的人開始):
k k+1 k+2 ... n-2, n-1, 0, 1, 2, ... k-2
並且從k開始報0.
現在我們把他們的編號做一下轉換:
k --> 0
k+1 --> 1
k+2 --> 2
.....
n-1 --> n-1-k
0--> n-k
...
...
k-3 --> n-3
k-2 --> n-2
從前面幾個數可以發現轉換後貌似都減了k,看到後面發現光減k不行,因為可能會出現負數,所以其實是先減去k再加上n最後對n取模。
變換後又從0開始報數,即就完完全全成為了(n-1)個人報數的子問題,假如我們知道這個子問題(即(n-1)個人報數的子問題)的解:例如x是最終出列的人,那麼根據上面這個表把這個x變回去就正好是n個人報數最後出列的人的編號x':
∵ k=m%n;
∴ x' = x+k = x+ m%n ; 而 x+ m%n 可能大於n
∴x'= (x+ m%n)%n = (x+m)%n 得到 x『=(x+m)%n
如何知道(n-1)個人報數的問題的解?對,只要知道(n-2)個人的解就行了。(n-2)個人的解呢?當然是先求(n-3)的情況 ---- 這顯然就是乙個倒推問題!好了,思路出來了,下面寫遞推公式:
令f表示i個人玩遊戲報m退出最後出列者的編號,最後的結果自然是f[n].
遞推公式: f[1]=0; f[i]=(f[i-1]+m)%i; (i>1)
#includeusing namespace std;
int main()
printf("%hd\n", s+1);
return 0;
}
參考: 約瑟夫環演算法
約瑟夫環是乙個數學的應用問題 已知n個人 以編號1,2,3.n分別表示 圍坐在一張圓桌周圍。從編號為k的人開始報數,數到m的那個人出列 他的下乙個人又從1開始報數,數到m的那個人又出列 依此規律重複下去,直到圓桌周圍的人全部出列。2b求解 private void myjosf int teamle...
演算法 約瑟夫環
已知n個人 以編號1,2,3 n分別表示 圍坐在一張圓桌周圍 使用 list 來模擬環結構,不被淘汰則將其加入到 list 尾部,淘汰的直接移除。需注意,以編號 k 為第乙個報數的人,需要調整其在報數正式開啟前到 list 的頭部。param n 人的總數 param k 開始報數的序號,1 k n...
演算法設計 約瑟夫環
本科系列課程參見 軟體學院那些課 約瑟夫 joeph 問題的一種描述是 編號為1,2,n的n個人按順時針方向圍坐一圈,每人持有乙個密碼 正整數 一開始任選乙個正整數作為報數上限值m,從第乙個人開始按順時針方向自1開始順序報數,報到m時停止報數。報m的人出列,將他的密碼作為新的m值,從他在順時針方向上...