1. 程式人生 > >高階搜尋演算法之迭代加深

高階搜尋演算法之迭代加深

# 前言 最開始搞 $OI$ 的時候接觸了搜尋演算法,後面基本上沒有在練過了。若本文有誤,請在討論區指出。 [本文例題連結](https://www.luogu.com.cn/problem/UVA529) # 思想 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2021020416083324.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ZhY2VfdGhlX0JsYXN0,size_16,color_FFFFFF,t_70#pic_center) 有時,答案不只一組,可能有多個,有些情況下需要找到有特殊情況的答案。 如上圖,需要找到的答案為 $ans3$ 。 首先考慮 $DFS$ ,一般是一搜搜到底,很有可能找到 $ans1$ 。若繼續查詢,很有可能花費太多時間。時間效率低。 再來考慮 $BFS$ ,它可以找到最近的答案 $ans2$ 。若繼續查詢,很有可能儲存狀態的佇列會浪費巨大空間。空間效率低 現在引出 $IDDFS$ ,它通常適用於有兩個條件的問題:一是它是個最優解問題,二是最優的答案深度最小。且能夠快速地找到答案。 假設在搜尋樹中,每層樹都有 $3$ 個方案,即是搜尋樹為一顆 $3$ 叉樹,共 $2$ 層, $ans$ 在 $3$ 。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2021020416254885.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ZhY2VfdGhlX0JsYXN0,size_16,color_FFFFFF,t_70#pic_center) 先來對比 $DFS$ ,搜尋路徑為 $1-2-5-2-6-2-7-2-1-3$ ,找到答案。有最壞情況,即每一個分支都是一個無底洞,若永遠搜尋不到答案,就會卡在裡面。 再來對比 $BFS$ ,搜尋路徑為 $1-2-3$ ,看起來比較短,但是佇列中有 $1,2,5,6,7,3$ 的資訊,若答案更深一些,那麼就會炸空間。 通過上述兩個例子,可以知道 $DFS$ 和 $BFS$ 的侷限性,但也各有千秋。結合兩種演算法,就有了迭代加深。首先限定一個層數,對於搜尋樹進行深度優先搜尋。假設這個層數為 $1$ ,那麼深搜只會搜尋到 $2$ ,不會繼續加深。首先試探性地來找答案,直到找到答案位置。很明顯,上面幾層的點會搜到很多遍,但時間複雜度對於 $DFS$ 來說比較優,而在空間複雜度上比 $BFS$ 上略勝一籌。 很容易就寫出模板: ```cpp int max_depth = min_depth; Id_Dfs( int current_depth , int max_depth ) { if( current_depth > max_depth ) return ; if( 找到答案 ){ 輸出答案 ; (exit(0) ; || return ;) } for each ( 當前節點的兒子節點 ) Id_Dfs(current_depth + 1, max_depth) ; } for(; ; max_depth++ ) { Id_Dfs( 0 , i ) ; } ``` 結合例題理解。 # 題目 一個與 $n$ 有關的整數加成序列 $$ 滿足以下四個條件: 1. $a_0=1$ 2. $a_m=n$ 3. $a_0= 1; i--) { for(int j = nowdepth; j >= i; j--) {//兩兩搭配,且答案越大越容易找到解,故而到著找 if(ans[i] + ans[j] <= n && ans[i] + ans[j] > ans[nowdepth]) {//滿足題意1,2兩點的搜尋 int now;//找到下一項 ans[nowdepth + 1] = now = ans[i] + ans[j]; for(int k = nowdepth + 2; k <= limit; k++) //從nowdepth + 1這一項開始,後面最大時也就是now不停擴大2倍,若最大都達不到n,捨去不求 now <<= 1; if(now < n) continue; Id_Dfs(nowdepth + 1);//搜尋下一層 if(flag)//找到答案 return; } } } ``` # C++程式碼 ```cpp #include #include bool Quick_Read(int &N) { N = 0; int op = 1; char c = getchar(); while(c < '0' || c > '9') { if(c == '-') op = -1; c = getchar(); } while(c >= '0' && c <= '9') { N = (N << 1) + (N << 3) + (c ^ 48); c = getchar(); } N *= op; return N != 0; } void Quick_Write(int N) { if(N < 0) { putchar('-'); N = -N; } if(N >= 10) Quick_Write(N / 10); putchar(N % 10 + 48); } const int MAXN = 1e5 + 5; int ans[MAXN]; int limit; bool flag; int n; void Id_Dfs(int nowdepth) { if(nowdepth > limit || flag)//達到層數不在戀戰或找到答案,直接跳出 return; if(ans[nowdepth] == n) {//滿足題意 flag = true; return; } for(int i = nowdepth; i >= 1; i--) { for(int j = nowdepth; j >= i; j--) {//兩兩搭配,且答案越大越容易找到解,故而到著找 if(ans[i] + ans[j] <= n && ans[i] + ans[j] > ans[nowdepth]) {//滿足題意1,2兩點的搜尋 int now;//找到下一項 ans[nowdepth + 1] = now = ans[i] + ans[j]; for(int k = nowdepth + 2; k <= limit; k++) //從nowdepth + 1這一項開始,後面最大時也就是now不停擴大2倍,若最大都達不到n,捨去不求 now <<= 1; if(now < n) continue; Id_Dfs(nowdepth + 1);//搜尋下一層 if(flag)//找到答案 return; } } } } void Work() { for(; !flag; limit++)//直到找到答案時停止搜尋 Id_Dfs(1); for(int i = 1; i < limit; i++) {//輸出 Quick_Write(ans[i]); putchar(' '); } putchar('\n'); } void Init() { limit = 1; int test = 1; while(test < n) {//找到最小層數 test <<= 1; limit++; } ans[1] = 1; flag = false; } int main() { while(Quick_Read(n)) {//多組輸入輸出,到0為止 Init(); Work(); } return