約瑟夫問題是個著名的問題:N個人圍成一圈,第一個人從1開始報數,報到k的人將被殺掉,接着下一個人又從1開始報,直到最后剩下一個,求最后留下的人的下標。
題目集合
解法1:暴力
可以直接暴力求解,時間復雜度為O(nk)
解法2:遞推
設$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:
如果當前這一位人沒被殺掉,則他可以放在幸存者的末尾,直到幸存者數量為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。