約瑟夫環問題的三種解法


約瑟夫問題是個著名的問題:N個人圍成一圈,第一個人從1開始報數,報到k的人將被殺掉,接着下一個人又從1開始報,直到最后剩下一個,求最后留下的人的下標。

題目集合

G - Josephus Problem

Josephus again

解法1:暴力

可以直接暴力求解,時間復雜度為O(nk)

解法2:遞推

reference

設$f(n,k)$為當n個人圍成一圈時,最后留下的人的下標。 對於$f(n-1,k)$來說,其結果相當於$f(n,k)$的結果向前移動$k\%(n-1)$位。

因為對於$f(n,k)$來說,去掉第一輪報的數$(k\%n)$后,現在就只剩下$n-1$個數,並且是以$(k\%(n-1)+1)$作為第一個數,即所有數向前移動$k\%(n-1)$位。現在的結果就為$f(n-1,k)$

對於f(5,3)來說,其結果為4。

 

 

 當其去掉第一輪報的數后,其向前移動了$(3\%4)$位,以4為起始,$f(4,3)$結果為1,對應着$f(5,3)$的結果4向前移動了3位

 

 

 

 所以反過來看即為,即為$f(n-1,k)$的結果向后移動$k\%(n-1)$位

即$f(n+1,k)=(f(n,k)+k\%n)\%n$   (x下標從0開始,因為取模結果為[0,n-1])

時間復雜度為$O(n)$

ll josephus2(ll n,ll k)
{
    ll pos=0;
    for(int len=1;len<=n;len++)
    {
        pos = (pos+k)%len;
    }
    return pos+1;
}
遞推代碼

 

解法3:

reference

如果當前這一位人沒被殺掉,則他可以放在幸存者的末尾,直到幸存者數量為1

所以對於下標為i的人,如果在他前面已經被殺掉了q個人,那么他的新的下標為$n+q(k-1)+x,(1\leq x <k)$

如下圖所示,最后被淘汰的編號一定是$n*k$,所以幸存者最后的編號是$n*k$

 

 我們現在需要從幸存者最后的編號中恢復出最初編號

假設幸存者這一次的編號為​$pos_{i}$,在他后面包括他還有$x$位幸存者,則$[pos_{i-1},pos_{i})$間一定有x個不能被k整除的數

這樣才能使在他后面包括他還有$x$位幸存者。所以根據這一推論,我們即可恢復最初的幸存者位置。

ll josephus(ll n,ll k)
{
    ll ans = n * k;
    ll cnt = 1; // 當前位置r與上一個位置l,[l,r)中有多少個不能被k整除的數
    ll tmp;
    while (ans>n)
    {
        ll t1 = cnt/(k-1);
        ll t2 = cnt%(k-1);
        if(ans%k>t2)
        {
            tmp = ans-(k*t1)-t2;
        }
        else
        {
            ll res = ans%k+(k-1)-t2;
            tmp = (ans-ans%k)-(k*(t1+1))+res;
        }
        cnt = cnt+ceil((ans-1)/k)-ceil((tmp-1)/k);
        ans = tmp;
    }
    return ans;
}
代碼

時間復雜度不好算,反正挺快的,大概是$O(logn)$的幾十倍,參考鏈接寫的是$O(logn)$,orz。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM