約瑟夫問題是資訊學奧賽中的一類經典且重要的題型,在平常的測試中屢屢出現。
通常題設可抽象為:一開始有 $n $個人圍成乙個圈, 從 $1 $開始順時針報數, 報出 $m $的人被踢出遊戲.。然後下乙個人再從$ 1 $開始報數,直到只剩下乙個人。
或者:曾經有個人在他身邊,然而現在只剩他乙個人。$who$ $are$ $you$$?$ $who$ $am$ $i$$?$ $why$ $am$ $i$ $here$$? $走的越來越慢,人越來越少,可終於還是只剩乙個了呢。他們圍成一圈,隨機了乙個人作為$1$號,然後逆時針依次編號。$1$號開始報數,報到 $1$,他走了;然後$2$號開始報數,$2$號報了$1$,$3$ 號報了$2$ ,於是$3$ 號也走了……每一輪都從上一次出局的下乙個人開始報數,第 $i$輪從$1$ 報到$i$ ,報 $i$的人出局。直到只剩他乙個人。卻早已不記得他自己是誰。
針對不同的資料範圍,可以存在如下幾種做法:
1. $o(nm)$
$o(nm)$的複雜度適用於$n,m$都在$30000$以內的情況,此類題型較少,例如「約瑟夫遊戲」一題,$n,m<=30000$,由於隨著遊戲的不斷進行,需要列舉的人數越少,所以複雜度實際低於$o(nm)$。演算法思路:暴力模擬即可。
#includeusing暴力模擬約瑟夫問題namespace
std;
int t,n,m; bool v[1000100
];void
wk()
++num;
if(num==m)
v[pos]=1,++t,num=0
; }
++pos;
if(pos==n+1) pos=1
; }
}int
main()
2.$o(n)$
$o(n)$演算法已經適用於大多數約瑟夫問題,讓$n<=1e7$的資料範圍可以被輕鬆解決,考慮以任意一人為起點,選出第$m$個人後的編號變化,設起始$id==0$,選出第$m$個人後,$id->(id+m)$,再回歸到原來的圓形,設$i$表示第$i$輪遊戲,那麼整體的公式即為$(id+m)$%$(n-i+1)$。倒序列舉即可。也可以用$dp$方式實現,或者正序列舉,將公式改變為$(id+m)$%$(i+1)$,最後答案即為$id+1$。
#include#define re registero(n)遞推約瑟夫問題using
namespace
std;
intt,n,ans,m;
inline
intread()
signed main()
for(re int i=n;i>=1;--i)
ans=(ans+m)%(n-i+1
); printf(
"%d\n
",ans+1
); }
return0;
}
3.$o(mlogn)$
此類演算法並不常見,但由於一些毒瘤出題人緣故,針對$n<=1e9,m<=1e5$型別的資料範圍,我們不得不採用特別的遞推方式,通過打表可以發現,保持$m$不變,$n$每加一,答案在模$n$意義下加$m$,注意:此時的$n$是乙個變化的$n$,那麼可以通過對$n$的遞推處理,將$o(n)$級別的列舉,轉化為在答案值域區間上的選擇性跳躍,從而將以$n$為基礎的演算法轉向以$m$為基礎的演算法,可以處理該類毒瘤問題。
#include#define int long longo(mlogn) 基於值域的約瑟夫問題#define re register
using
namespace
std;
intt,n,m;
inline
intread()
signed main()
now=now+nxt+1
; ans=(ans+(nxt+1)*m-1)%now+1
; }
printf(
"%lld\n
",ans);
}return0;
}
4.$o(log_m^n)$
此類演算法極其不常見,僅適用於$n$個人圍成一圈,從$1$號開始依次報數,當報到$m$時,報$1$、$2$、…、$m-1$的人出局,下乙個人接著從$1$開始報,保證$(n-1)$是$(m-1)$的倍數,最後剩的乙個人獲勝的情況。通過打表,可以發現,$f[m^a+m+1]=m$,其餘的$f[n]$都滿足$f[n][n-m+1]$,不妨$ n=m^a+(m-1)*k(m^a
1 #include2約瑟夫問題using
namespace
std;
3long
long
n,m;
4signed main()
至此,通過不同的資料範圍選擇不同的演算法,一般的約瑟夫問題已經可以完全解決。
$over$
關於RMQ問題的四種解法
什麼是rmq問題 rmq range minimum maximum query 對於長度為n的陣列a,回答若干詢問rmq a,i,j i,j n 1 返回陣列a中下標在i,j範圍內的最小 大 值,也就是說,rmq問題是指求區間最值的問題。1.暴力法最簡單的方法,就是遍歷陣列直接搜尋,但是這種方式時...
經典演算法 約瑟夫環問題的三種解法
約瑟夫環問題,這是乙個很經典演算法,處理的關鍵是 偽鍊錶 問題描述 n個人圍成一圈,從第乙個人開始報數,報到m的人出圈,剩下的人繼續從1開始報數,報到m的人出圈 如此往復,直到所有人出圈。模擬此過程,輸出出圈的人的序號 在資料結構與演算法書上,這個是用鍊錶解決的。我感覺鍊錶使用起來很麻煩,並且這個用...
揹包問題 四種解法解題
分別用蠻力法 動態規劃法 回溯法和分支限界法求解0 1揹包問題。注 0 1揹包問題 給定種物品和乙個容量為的揹包,物品的重量是,其價值為,揹包問題是如何使選擇裝入揹包內的物品,使得裝入揹包中的物品的總價值最大。其中,每種物品只有全部裝入揹包或不裝入揹包兩種選擇。1 基本思想 對於有n種可選物品的0 ...