[學習筆記]搜索——模擬與dp的結合
搜索:
一種基礎的算法。
考察常見於NOIP
但是高級的搜索算法可能還會在省選出現。
50%以上的暴力都可以用搜索直接枚舉來寫。
但是,當數據規模不是很大的時候,搜索也可能成為正解。
(比如剪枝PK狀壓dp)
在搜索的基礎上,可以衍生出最短路,而dp本質上,也是搜索的剪枝。
一、基礎搜索算法
DFS:
最基本的搜索。用遞歸實現。
顧名思義,深度優先搜索的特點就是從一個位置直接搜下去,直到搜到末尾或者中途return
先擴展出深度。
dfs圖中遍歷的狀態會形成一棵搜索樹。(如果搜成了一般圖,那就說明剪枝不到位了)
一般認為,dfs的復雜度就是“搜索樹的大小 乘上 每一個狀態 下的復雜度”
自我感覺,dfs的更多時候,用途不在於搜索,而在於對結構的遍歷。
用途:
1.50%的暴力,2^n,n!的枚舉。
2.容斥。
3.遍歷。
dfs遍歷一棵樹(可能爆棧),(求dfs序,樹形dp等)
因為dfs會搜完一棵子樹再回溯,所以利用這個性質,dfn,dfn2,以及tarjan都可以成立。
利用遞歸的性質,從兒子回溯後,對這一層的檢查與更新,也是經常用到的。
4.配合BFS,尋找聯通塊
BFS:
最基本的遍歷圖的方法。一般用於搜索。
顧名思義,廣度優先搜索,就是先擴展整個圖的層數。
會從一個起點(多個起點)開始,不斷把周圍一層遍歷,
即,遍歷的狀態的層數總是連續的一段。
用途:
1.邊權為1的最短路。
這個其實是值得註意的,BFS的最短路是O(n+m)的,是所有的最短路中最快的。(有時那SPFA或者dij跑邊權為1的最短路,就浪費了~~~)
當然,必須邊權是1。
2.分層圖:
利用BFS的分層圖的性質,可以有層次地遍歷一個結構。
AC自動機的fail樹的構建:由於分層圖,而fail[i]一定長度比i小,即層的編號小,所以必然已經遍歷,沒有後效性。
DINIC算法,在分層圖上跑增廣路。可以保證復雜度。
問題:
1.DFS的劣勢明顯,會進入一個搜索子樹,遍歷完這棵搜索樹之後才會回溯。
如果這棵搜索樹對答案不能產生影響,那麽會大大降低效率。
更糟糕的,如果一棵子樹非常龐大(指數級增長),則直接TLE地飛起。
2.BFS的劣勢明顯,由於要廣度優先搜索,所以會遍歷完整個一層才會遍歷下一層。
如果一層很多,也會爆炸。
而且,必須記錄所有當前在隊列裏狀態的狀態信息。
如果狀態增多,搜索樹較大,空間和時間都沒有辦法保證。
對於這些bug,機智的人們創建了新的優化方法。
二、剪枝
剪枝,顧名思義,就是在dfs或者bfs中,把一棵搜索子樹直接砍去,不進行遍歷。
來達到復雜度的保證。
其實剪枝範圍可以很廣,A*,IDA*,甚至dp,我認為都可以叫剪枝。
而且剪枝也不一定用於搜索。
當然,一般情況下的剪枝,就是指用dfs搜索中的剪枝。
1.最優性剪枝。
最常見。最基本的,對於單增取min,單減取max,都可以穩定減去一些復雜度。
配合估價函數,對未來最少花費進行預估,通常可以大大增加效率。
2.可行性剪枝。
對於狀態搜索下去是否能合法的剪枝。
剪枝最重要的還是分析題目的性質。
例題:NOIP2004 蟲食算
Mayan遊戲
生日蛋糕
[學習筆記]搜索——模擬與dp的結合