搜尋
有一個lq暴論:會搜尋就能拿省一。其實也沒什麼問題,但是搜尋掌握的太差了,目前還是隻會回溯法和一點點的迭代加深。
回溯
這個是搜尋最基本的操作了,應該不需要贅述。
折半搜尋
當一個搜尋樹的深度足夠深,我們就會花上 \(dep\) 的指數級代價。
但是當我們的始末態明確的時候,我們可以從頭開始搜一半的深度,從尾開始搜一半的深度,如果兩種能夠拼接在一起,就說明這是一種合理的解法。
例題
- P4799
- CSP2022T1
這兩道例題裡面的折半拼接方法大概是,把每一半可能的答案都分別存進陣列裡面,然後用大概類似於雙指標或者是二分的辦法來貪心選取拼接之後的答案。
A*
一個甚至在競賽之外也有很多應用的演算法,比如MOBA類遊戲中的尋路。
就是給狀態設計一個估值函式,然後根據估值函式來對狀態進行優劣排序,存在一個優先佇列裡面進行廣搜。
這裡摘一段 OI WIKI 的原文 :
定義起點 \(S\) ,終點 \(T\) ,從起點(初始狀態)開始的距離函式 \(G(x)\) 到終點(最終狀態)的距離函式 \(H(x)\) ,以及每個點的估價函式
\(f(x)=G(x)+H(x)\)
A * 演算法每次從優先佇列中取出一個 \(f\) 最小的元素,然後更新相鄰的狀態。
如果 \(H<H_{實際}\) ,則 A * 演算法能找到最優解。(這是極其重要的一點) 這其實是意味著 在搜尋的過程中不會錯過最優解
當\(H=0\) 時,A * 演算法變為 Dijkstra;當 \(H=0\) 並且邊權為 \(1\) 時變為 BFS。
(因此可以透過BFS快速求簡單邊權為 \(1\) 的圖的最短路)。
//TODO
迭代加深
顧名思義就是給搜尋樹限定一個深度。
void dfs(int x,int dep,int limit)
{
if(dep>limit)return;
else dfs(....);
}
int main()
{
for(int dep=1;dep<=n;++dep)
dfs(start,0,dep);
return 0;
}
這樣的話就不至於一條路走到黑了,當然只有在答案足夠小的時候才能夠使用(或者說是資料範圍足夠小)
例題
- 埃及分數
IDA*
這個可以顧名思義一下,實際上就是把 A* 和 迭代加深 套在一起了。
有段時間非常喜歡寫這個,但是缺點就在於有些重複的東西還是會搜很多次。
例題
- 埃及分數