poj 2359 約瑟夫環數學問題

2021-06-23 09:49:41 字數 1334 閱讀 3464

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

為了討論方便,先把問題稍微改變一下,並不影響原意:

問題描述:n個人(編號0~(n-1)),從0開始報數,報到(m-1)的退出,剩下的人繼續從0開始報數。求勝利者的編號。

我們知道第乙個人(編號一定是m%n-1) 出列之後,剩下的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

......

k-2   --> n-2

k-1   --> n-1

變換後就完完全全成為了(n-1)個人報數的子問題,假如我們知道這個子問題的解:例如x是最終的勝利者,那麼根據上面這個表把這個x變回去不剛好就是n個人情況的解嗎?!!變回去的公式很簡單,相信大家都可以推出來:x'=(x+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-n順序算出f[i]的數值,最後結果是f[n]。因為實際生活中編號總是從1開始,我們輸出f[n]+1

由於是逐級遞推,不需要儲存每個f[i],程式也是異常簡單:

#include #include #include using namespace std;

int main()

num=0;

len=s.length();

for(int i=2;i<=len;i++)

num=(num+n)%i;

if(s[num]=='?')

printf("yes\n");

else if(s[num]==' ')

printf("no\n");

else

printf("no comments\n");

return 0;

}

POJ2359(約瑟夫環)

解題思路 題目雖然很長,其實就是乙個約瑟夫環問題,對於輸入的字串,從開始依次進行報數,當報到 n 1999時,刪除對應的字元,字串可以看作是首尾相連的環,直到剩餘最後乙個字元。如果剩餘字元為 則輸出結果 yes 如果剩餘字元為 則輸出結果 no 否則輸出結果 no comments 注意 本題只有一...

約瑟夫環問題數學解法

首先一開始的序列 序列1 1,2,3,4,n 2,n 1,n 此時出佇列的第乙個人,位置為k,號碼肯定是m n。這個應該沒有問題,也就是取餘操作使得陣列類似能夠有迴圈的功能。此時序列2 1,2,3,4,k 1,k 1,n 2,n 1,n 此時k出佇列,序列2中為n 1個人了。根據序列2,得到序列3 ...

POJ 1845 數學問題

求a b的所有約數 即因子 之和,並對其取模 9901再輸出。0 a,b 50000000 我們首先要用到這樣乙個定理,數字a的所有因數之和,對於已經分解的整數a p1 k1 p2 k2 p3 k3 pn kn 有a的所有因子之和為 s 1 p1 p1 2 p1 3 p1 k1 1 p2 p2 2 ...