已知n個人(以編號1,2,3…n分別表示)圍坐在一張圓桌周圍。從編號為1的人開始報數,數到m的那個人出列;他的下乙個人又從1開始報數,數到m的那個人又出列;依此規律重複下去,直到圓桌周圍的人全部出列。(也類似於**殺人狂問題)通常解決這類問題時我們把編號從0~n-1,最後結果+1即為原問題的解。通常,我們會要求輸出最後一位出列的人的序號。那麼這裡主要研究的是最後乙個出列的人的序號要怎麼確定。
1、陣列模擬
2、鍊錶模擬
3、數學推導
當n,m資料量很小的時候,我們可以用迴圈鍊錶模擬約瑟夫環的過程。當模擬到人數等於1的時候,輸出剩下的人的序號即可。
這種方法往往實現起來比較簡單,而且也很容易理解。但是時間複雜度卻是很糟糕的,達到了o(nm),這樣的話,其實在n,m比較大的時候(nm達到10^8或者更大),那麼要得出結果往往需要耗費很長的時間,但是我們可以運用一點數學上的技巧,將最後結果推導出來。
class
solution
int child[n]
;for
(int j =
0; j < n; j++
)int i =0;
while(!
check
(child, n))if
(j == m-1)
i++; i = i % n;}}
int ans =0;
for(
int j =
0; j < n; j++)}
return ans;
}bool
check
(int
*child,
int n)
if(count >1)
}return flag;}}
;
為了簡化出列的過程:
首先我們把這n個人的序號編號從0~n-1(理由很簡單,由於m是可能大於n的,而當m大於等於n時,那麼第乙個出列的人編號是m%n,而m%n是可能等於0的,這樣編號的話能夠簡化後續出列的過程),當數到m-1的那個人出列,因此我們編號完成之後,開始分析出列的過程:
第一次出列:
一開始的時候,所有人的編號排成序列的模式即為:
0,1,2,3,4,5…n-2,n-1
那麼第一次出列的人的編號則是(m-1)%n1,那麼在第乙個人出列之後,從他的下乙個人又開始從0開始報數,為了方便我們設k1 = m%n1(n1為當前序列的總人數)那麼在第乙個人出列之後,k1則是下一次新的編號序列的首位元素,那麼我們得到的新的編號序列為:
k1,k1+1,k1+2,k1+3…n-2,n-1,0,1,2…k1-3,k1-2 (k1-1第一次已出列)
那麼在這個新的序列中,第乙個人依舊是從0開始報數,那麼在這個新的序列中,每個人報的相應數字為:
0,1,2,3…n-2
那麼第二次每個人報的相應數字與第一次時自己相應的編號對應起來的關係則為:
0 --> k1
1 --> k1+1
2 --> k1+2
…n-2 —> (k1+n-2)%n1(n1為當前序列的總人數,因為是迴圈的序列,k1+n-1可能大於總人數)
那麼這時我們要解決的問題就是n-1個人的報數問題(即n-1階約瑟夫環的問題)
可能以上過程你還是覺得不太清晰,那麼我們重複以上過程,繼續推導剩餘的n-1個人的約瑟夫環的問題:
那麼在這剩下的n-1個人中,我們也可以為了方便,將這n-1個人編號為:
0,1,2,3,4…n-2
那麼此時出列的人的編號則是(m-1) % n2(n2為當前序列的總人數),同樣的我們設k2 = m % n2,那麼在這個人出列了以後,序列重排,重排後新的編號序列為:
k2,k2+1,k2+2,k2+3…n-2,n-1,0,1,2…k2-3,k2-2 (k2-1第一次已出列)
那麼在這個新的序列中,第乙個人依舊是從1開始報數,那麼在這個新的序列中,每個人報的相應數字為:
1,2,3,4…n-2
那麼這樣的話是不是又把問題轉化成了n-2階約瑟夫環的問題呢?
後面的過程與前兩次的過程一模一樣,那麼遞迴處理下去,直到最後只剩下乙個人的時候,便可以直接得出結果
當我們得到乙個人的時候(即一階約瑟夫環問題)的結果,那麼我們是否能通過一階約瑟夫環問題的結果,推導出二階約瑟夫環的結果呢?
借助上面的分析過程,我們知道,當在解決n階約瑟夫環問題時,序號為k1的人出列後,剩下的n-1個人又重新組成了乙個n-1階的約瑟夫環,那麼
假如得到了這個n-1階約瑟夫環問題的結果為ans(即最後乙個出列的人編號為ans),那麼我們通過上述分析過程,可以知道,n階約瑟夫環的結果
(ans + k)%n(n為當前序列的總人數),而k = m%n
則有:n階約瑟夫環的結果
(ans + m % n)%n,那麼我們還可以將該式進行一下簡單的化簡:
當m而當m>=n時,那麼上式則化簡為:(ans % n + m%n%n)% n
即為:(ans % n + m%n)% n
而 (ans + m)% n = (ans % n + m%n)% n
因此得證
(ans + m % n)%n = (ans + m)% n
這樣的話,我們就得到了遞推公式,由於編號是從0開始的,那麼我們可以令
f[1] = 0; //當乙個人的時候,出隊人員編號為0
f[n] = (f[n-1] + m)%n //m表示每次數到該數的人出列,n表示當前序列的總人數
而我們只需要得到第n次出列的結果即可,那麼不需要另外宣告陣列儲存資料,只需要直接乙個for迴圈求得n階約瑟夫環問題的結果即可
由於往往現實生活中編號是從1-n,那麼我們把最後的結果加1即可。
class
solution
int ans =0;
for(
int i =
2; i <= n; i++
)return ans;}}
;
約瑟夫問題 約瑟夫環
約瑟夫 問題 有時也稱為約瑟夫斯置換,是乙個出現在電腦科學和數學中的問題。在計算機程式設計的演算法中,類似問題又稱為約瑟夫環。又稱 丟手絹問題 據說著名猶太歷史學家 josephus有過以下的故事 在羅馬人占領喬塔帕特後,39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死...
約瑟夫問題 約瑟夫環
約瑟夫問題 有時也稱為約瑟夫斯置換,是乙個出現在電腦科學和數學中的問題。在計算機程式設計的演算法中,類似問題又稱為約瑟夫環。又稱 丟手絹問題 據說著名猶太歷史學家 josephus有過以下的故事 在羅馬人占領喬塔帕特後,39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死也...
約瑟夫環問題
約瑟夫環問題 問題描述 編號是1,2,n的n個人按照順時針方向圍坐一圈,每個人持有乙個密碼 正整數 一開始任選乙個正整數作為報數上限值m,從第乙個人開始順時針方向自1開始順序報數,報到m時停止報數。報m的人出列,將他的密碼作為新的m值,從他在順時針方向的下乙個人開始重新從1報數,如此下去,直到所有人...