迭代加深搜索学习笔记
我们都知道 dfs 是一头搜到底,这样在搜索树深度未知时可能会搜的很慢。而我们又知道 bfs 是一层一层搜,这样可以免去搜索树深度未知的影响。但是 bfs 使用了一个队列维护状态,在状态很大时(比如整个棋盘)就会爆内存。
那有没有两全其美的办法呢?
有,它就是迭代加深搜索。
具体先看一道题:Addition Chains
其实迭代加深搜索很简单,名字挺高大上的,本质上就是一个 dfs 套一个最大深度判断,它在搜索的时候每次会判断当前的深度(搜过的步数)是否大于他设定的最大深度,如果是则直接跳出,最后返回搜到了答案没(满足所有条件)。
对于这道题,假设我们在搜 \(m\leq x\) 时满足条件,搜 \(m \leq x-1\) 时又不满足,则一定 \(m=x\),因为深度大的搜索树一定把深度小的搜索树包含了(类似二分)。
可能不好理解,看一下代码就知道了。
注意,这里是 \(\textit{\textbf{n}}\) 较小时的解法,如果要通过所有数据就得剪枝。
#include <iostream>
using namespace std;
int n;
int a[210];
int b[210];
bool ans;
void dfs(int p,int l) {
if(ans) {
return;
}
if(p>=l) {
if(a[p-1]==n) {
ans=true;
}
for(int i=0;i<p;i++) {
b[i]=a[i];
}
return;
}
for(int i=0;i<p;i++) {
for(int j=i;j<p;j++) {
a[p]=a[i]+a[j];
if(a[p]>n) {
break;
}
if(a[p]<=a[p-1]) {
continue;
}
dfs(p+1,l);
}
}
}
int main() {
while(cin>>n,n) {
a[0]=1;
for(int i=1;;i++) {
ans=false;
dfs(1,i);
if(ans) {
for(int j=0;j<i;j++) {
cout<<b[j]<<" ";
}
cout<<endl;
break;
}
}
}
return 0;
}
我们知道深度大的搜索树会包含深度小的搜索树,所以那些深度小的状态可能会被搜很多次,在某些情况下会超时。我们设能搜到答案的最大深度最小的这次 dfs 的复杂度大致是 \(O(n^{m})\),则整个迭代加深搜索的复杂度就是 \(O(\sum_{i=1}^{m}n^{i})\),有时还会比普通的 dfs 慢。所以在你觉得答案很大的时候,最好还是不要考虑搜索。对于上面那道题,答案一般是 \(10\) 左右,所以可以使用迭代加深搜索。
习题:全谷只有两道有迭代加深搜索标签的题,自己去做吧......
IDA* 算法就是迭代加深搜索的一个应用。
其实 IDA* 虽然和 A* 名字有点像,但相同点只有估价函数(不懂的一会解释)。主要是因为 IDA* 是 dfs 改良的,A* 是 bfs 改良的。
回归算法本身,要弄懂 IDA,首先得知道估价函数。估价函数其实就是一个评估状态好坏的函数,大部分时候就是离答案的距离(要走多少步才能到答案)。当然,估价函数有一个“估”字,不需要算的太准确。要是能完全算的准确,还要 IDA 干啥。我们设估价函数评估当前状态的返回值为 \(x\),当前走了 \(step\) 步,设置的最大深度为 \(m\),则在 \(x=0\) 时就找到了答案,如果 \(step+x>m\) 就可以直接跳出。
第一个很好理解,第二个是为啥?
因为当前已经走了 \(step\) 步,要走到答案至少要走 \(x\) 步,那么走到答案的深度肯定已经超过了 \(step+x\) ,但 \(step+x\) 都比 \(m\) 大,所以走到答案的深度肯定比 \(m\) 大。
发现了吗,只有估价函数估出的结果小于等于真正的结果才行。所以在写估价函数时一定要注意,不然可能会把正确的答案给跳出。
放一道题:骑士精神
这道题就是 IDA* 的众多模板中的一道。这题的估价函数就是计算当前状态与最终状态不同的位置个数(空格也算),dfs 时三个参数分别表示空格的 \(x\) 坐标,\(y\) 坐标和当前深度,其他的看代码理解。
#include <iostream>
using namespace std;
int T,m;
int a[10][10];
int b[10][10]={
{0,0,0,0,0,0},
{0,1,1,1,1,1},
{0,0,1,1,1,1},
{0,0,0,2,1,1},
{0,0,0,0,0,1},
{0,0,0,0,0,0}
};
int fx[8][2]={
{-2,1},{-2,-1},{2,1},{2,-1},
{1,-2},{-1,-2},{1,2},{-1,2}
};
bool flg=false;
int gj() {
int ans=0;
for(int i=1;i<=5;i++) {
for(int j=1;j<=5;j++) {
if(a[i][j]!=b[i][j]) {
ans++;
}
}
}
return ans;
}
bool in(int x,int y) {
return x>=1&&x<=5&&y>=1&&y<=5;
}
void dfs(int p,int x,int y) {
int tmp=gj();
if(tmp==0) {
flg=true;
}
if(flg) {
return;
}
if(p+tmp>m+1) {
return;
}
for(int i=0;i<8;i++) {
int nx=x+fx[i][0];
int ny=y+fx[i][1];
if(!in(nx,ny)) {
continue;
}
swap(a[x][y],a[nx][ny]);
dfs(p+1,nx,ny);
swap(a[x][y],a[nx][ny]);
}
}
void solve() {
int x,y;
flg=false;
for(int i=1;i<=5;i++) {
for(int j=1;j<=5;j++) {
char ch;
cin>>ch;
if(ch=='*') {
a[i][j]=2;
x=i;
y=j;
} else {
a[i][j]=ch-'0';
}
}
}
for(m=0;m<=15;m++) {
dfs(0,x,y);
if(flg) {
cout<<m<<endl;
return;
}
}
cout<<-1<<endl;
return;
}
int main() {
cin>>T;
while(T--) {
solve();
}
return 0;
}
习题:全谷只有六道有 IDA* 标签的题,自己去做吧......
感谢观看!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)