引入
考虑这样一个情况:当你在用DFS遍历一棵树找答案。
但是这棵树比较恶心。
终点虽然说在红色的点,但是一般人的代码先遍历左儿子就会进入左下方那一堆,但是实际上答案离起点很近。
IDDFS
所以,根据上面的情况,我们可以限制一下搜索的深度,超过了深度后就不管了,这样就可以避免无用又浪费时间的搜索。
当我们搜索的对象是一棵树时,找到目标点的时候,此时的深度就是从根节点找到答案的步数。
因此,我们在dfs函数里面加上一个参数
d
d
d,表示当前搜索的深度,如果其超过了限制深度,就不用管了,直接return。
这就是IDDFS的思想。
IDA*
IDDFS讲完了,那么IDA* 又是什么?
其实就是IDDFS+剪枝。
BFS的A*1是给定准确目标,优化掉不必要的遍历,这和剪枝的思路也是一样的。
例题
P10494 [USACO02FEB] Power Hungry Cows
先考虑暴力的思路,已经得到的数存在一个数组里,然后用当前的指数依次去加,找到答案了就return。
然后是IDDFS的深度限制
for(dep=0;;dep++)if(dfs(1,0))break;
第一个参数是最开始的指数,第二个参数是深度
最后的深度
d
e
p
dep
dep就是需要的次数。
接下来考虑剪枝。
对于一个指数
x
x
x,可以出现的最大情况一定是加上自己变成
2
x
2x
2x,接下来的最大就是变成
4
x
4x
4x,
8
x
8x
8x,
16
x
16x
16x……
所以,对于指数
x
x
x,进行
k
k
k次操作的情况下,最大可以达到
x
⋅
2
k
x\cdot2^k
x⋅2k。
那么,对于再执行
k
k
k步的情况下,如果
x
⋅
2
k
x\cdot2^k
x⋅2k都没法达到目标,就不必再搜索下去了。
同理,当
x
2
k
\frac{x}{2^k}
2kx都大于目标,也没必要再搜索了。
if(x<<(dep-d)<n)return 0;
if(x>>(dep-d)>n)return 0;
return 0意思是不满足条件,继续加深搜索深度。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=55;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
int n;
int dep;
int a[N];
bool dfs(int x,int d){
if(x==n)return 1;//达到目标
if(d>=dep)return 0;//超过深度
//剪枝
if(x<<(dep-d)<n)return 0;
if(x>>(dep-d)>n)return 0;
a[d]=x;
for(int i=d;i>=0;i--){//从大往小遍历,尽量减少搜索次数
if(dfs(x+a[i],d+1))return 1;
else if(dfs(x-a[i],d+1))return 1;
}
return 0;
}
signed main(){
n=read();
for(dep=0;;dep++)if(dfs(1,0))break;
print(dep);
}