迭代加深和雙向搜尋

2021-08-21 12:37:14 字數 3856 閱讀 7639

當答案的層數較低,並且搜尋的分支較多時,如果直接搜尋會消耗很多時間。這時候可以進行多次搜尋,每次搜尋可以限制乙個深度,如果我們在當前深度下搜尋不到答案,就增加深度限制,重新搜尋一邊答案,這樣「迭代」且「加深」的過程稱為迭代加深。但他的缺點也很明顯,每次需要重新搜尋一遍,所以在答案的層數比較深的時候不建議使用。

【例題】addition chains (poj2248)

需要求乙個長度(長度為m)最小的序列

a a

滿足 1、

a[1]

=1' role="presentation">1、a

[1]=

11、a

[1]=

12、a

[m]=

m 2、a

[m]=

m3、a

[1]2]

<..

...a

[m−1

]m]3 、a

[1

]

2]

<..

...a

[m−1

]

m]

4、對於每個a

[k](

2≤k≤

m)都存

在a[k

]=a[

i]+a

[j](

1≤i,

j≤k−

1,i,

j可以相

等)4 、對

於每個a

[k](

2≤k≤

m)都存

在a[k

]=a[

i]+a

[j](

1≤i,

j≤k−

1,i,

j可以相

等)

其中n(

n≤100)

n (n

≤100

)是給定的值

搜尋思路大概是列舉每個k進行搜尋,把i,j作為分支填寫到a[k]上進行搜尋。

套路剪枝一下,倒序列舉,a[i]+a[j]判重。

由於n比較小,且要滿足第四條條件,所以長度m的長度不會太大,所以深度不會太大,由於所有小於k的位置都可以作為i,j所以搜尋的分支比較多。那麼我們就可以使用迭代加深來優化速度。

#include

#include

#include

#include

#include

#include

using

namespace

std;

int n;

int a[110];

bool v[110][110];

int dep;

bool flag;

void dfs(int k)

memset(v[k],false,sizeof(v[k]));

for(int i=k-1;i>=1;i--)}}

}int main()

}}

雙向搜尋又名折半搜尋。當搜尋的複雜度在指數級的時候,我們可以通過將指數折半的方法降低搜尋複雜度。

具體做法是從初態和終態出發各搜尋一半狀態,產生兩顆深度減半的搜尋樹,兩顆樹交匯在一起形成最終答案,將o(

nk) o(n

k)

降低到o(

nk/2

+nk/

2+1)

o (n

k/2+

nk/2

+1

)的複雜度。

其實對於這樣的指數級複雜度,如果指數除以2後可以接受的話,可以考慮雙向搜尋。

【例題】燈

傳送門

雙向搜尋的例題,首先狀壓燈的開關狀態。我們知道起點狀態時全滅,終點狀態是全亮,那麼就可以分成兩半搜尋,搜到相反數即可。假設有四個燈,我們把燈分成左右兩部分,從0000~1101我們只按左邊的按鈕,1111~0010只按右邊的,那加起來就是答案,可以更新最小步數。

那麼對於乙個點相連的所有點,我們可以對乙個點x記錄乙個二進位制f[x],將他相連的點(包括他自己)記為1,搜尋的時候記錄乙個當前狀態now,每次開關x的時候就可以讓now異或f[x],就可以得到開關這個點可以達到的狀態啦。

#include

#include

#include

#include

#include

using

namespace

std;

typedef

long

long ll;

const

int inf=2147483647;

ll bin[40];

ll f[40],ed;

bool half=false;

int cnt,minn;

mapint>s;//到now這個狀態需最少要多少步,折半後第二次加上第一次的值

void dfs(int x,ll now,int step)

else

//過半,看看now和其他合併能否到達最終狀態

}return;

}dfs(x+1,now^f[x],step+1);

dfs(x+1,now,step);

}int main()

minn=inf;

half=false;cnt=n/2; dfs(1,0,0);

half=true; cnt=n; dfs(n/2+1,0,0);

printf("%d\n",minn);

return

0;}

【例題】送禮物(tyvj1340)

在n個數中選若干個數,使得他們加起來小於等於w且最接近w,輸出這個最接近w的值。

( n<=45 w<=2^31-1)

可以看到w是很大的,所以不用揹包來做。

搜尋的話,對於乙個物品,樸素的有選和不選兩種選擇,那麼這樣的複雜度是o(

2n) o(2

n)

,但如我們上面所說,指數級複雜度如果折半可以接受的話,可以考慮雙向搜尋。我們發現o(

222+2

23)o (2

22+223

)是可以接受的,所以我們可以考慮折半搜尋。

首先,我們第一遍搜尋出前一半數中選出若干個可以達到的0~w之間的所有重量值,把他們存放在乙個有序陣列a中。

然後,第二遍在後一半選出若干個數,記錄他們的重量和now,對於乙個重量和now在a中二分查詢<=w-now中最大的乙個t,然後用t+now更新答案。

#include

#include

#include

#include

#include

#include

using

namespace

std;

typedef

long

long ll;

const

int n=50;

const

int inf=int_max;

int w;

int n,a[n];

int a[1

<<25],tmp=0;

int cnt;

int maxx;

bool half;

int cmp(int a,int b)

int erfen(int val)

else

r=mid-1;

}return ans;

}void dfs(int x,int now)

return;

}if((ll)now+a[x]<=w) dfs(x+1,now+a[x]);

dfs(x+1,now);

}int main()

迭代加深搜尋

深度優先搜尋每次選定乙個分支,然後往下搜尋,直到遞迴邊界 才回溯。這種策略有一點缺陷,那就是當搜尋樹的分支數目特別 多,並且答案在某個較淺的節點上,如果dfs在一開始就選錯了分 支,那就會在沒有答案的深層次浪費時間 當搜尋樹規模隨著層次的深入增長很快,並且能 夠確保答案在乙個較淺的節點上時,就可以使...

迭代加深搜搜尋

對於可以用回溯法求解但解答樹的深度沒有明顯上限的題目,可以考慮使用迭代加深搜尋。經典問題 埃及分數問題 給出乙個分數,比如19 45,把它寫成若干個形如1 ri的分數的和的形式,比如19 45 1 5 1 6 1 18,要求分母不能重複使用並且使用的分數的個數最少。如果有多組個數相同的解,最後的分數...

IDDFS迭代加深搜尋

includeusing namespace std const int maxn 10 int n,a maxn bool ans sort return true int h bool dfs int d,int maxd return false int solve return max an...