IDA*算法是在A*算法的迭代加深
A*算法主要用在bfs中,IDA*算法主要用在dfs中
先来看一道IDA*的例题
前置知识1:迭代加深
定义: 每次限定一个maxdep最大深度,使搜索树的深度不超过maxdep
for(int maxdep=1;maxdep<=题目中给的最大步数;maxdep++){
dfs(0,maxdep);//0为出入函数中当前步数,maxdep为传入的最大深度。
if(success)break;//如果搜索成功则会在dfs函数中将success赋值为1。
}
使用范围:
1.在有一定的限制条件时使用(例如本题中“如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出−1。“)。
2.题目中说输出所以解中的任何一组解。
为什么能够降低时间复杂度:
我们可能会在一个没有解(或解很深的地方无限递归然而题目中要求输出任何的一组解),所以我们限制一个深度,让它去遍历更多的分支,去更广泛地求解,(其实和BFS有异曲同工之妙)。
定义:f(n)=g(n)+h(n)
其中f(n)是节点的估价函数,g(n)是现在的实际步数,h(n)是对未来步数的最完美估价(“完美”的意思是可能你现实也可能不会实现,但你还要拿最优的步数去把h(n)算出来。
void dfs(int dep,int maxdep){
if(evaluate()+dep>maxdep)return;
//evaluate函数为对未来估价的函数,若未来估价加实际步数>迭代加深的深度则return。
if(!evaluate){
success=1;
printf("%d\n",dep);
return;
}
......
}
A∗是用于对BFS的优化;
IDA*是对结合迭代加深的DFS 的优化。
本质上只是在BFS和DFS上加上了一个估价函数。
何时使用因题而定。
现在就是要想一个比较好的估价函数(若估价函数不好的话,优化效率就并不高,例如若估价函数一直为0,那就是爆搜)。
然后来看这道题
我们可以想一下,每次空白格子和黑白棋子交换,最优的情况就是每次都把黑白棋子移动到目标格子。
所以评估函数:
const int goal[7][7] = {
{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 evaluate(){
int cnt=0;
for(register int i(1) ; i<=5 ; i=-~i){
for(register int j(1) ; j<=5 ; j=-~j){
if(mp[i][j] != goal[i][j]) cnt++; //如果这两个点不相同
}
}
return cnt; //返回最少理想情况下需要的次数
}
然后这道题就是普通的爆搜了
#include
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48) ; ch=getchar();}
return x*f;
}
const int M = 10;
const int dx[] = {1,1,-1,-1,2,2,-2,-2};
const int dy[] = {2,-2,2,-2,1,-1,1,-1};
int mp[M][M];
int T;
int n,m,success,sx,sy,flag;
const int goal[7][7] = {
{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 evaluate(){
int cnt=0;
for(register int i(1) ; i<=5 ; i=-~i){
for(register int j(1) ; j<=5 ; j=-~j){
if(mp[i][j] != goal[i][j]) cnt++;
}
}
return cnt;
}
int pd(int x,int y){
if(x<1||x>5||y<1||y>5) return 0;
return 1;
}
void A_star(int dep,int x,int y,int maxdep){
if(dep == maxdep){
if(!evaluate()) success=1; //表示正好完全匹配
return;
}
for(register int i(0) ; i<8 ; i=-~i){
int xx = x + dx[i];
int yy = y + dy[i]; //8个方向
if(!pd(xx,yy)) continue; //不能走就跳过
swap(mp[x][y],mp[xx][yy]); //把两个点的颜色交换
int eva = evaluate(); //理想情况下的最少步数
if(eva + dep <= maxdep) A_star(dep+1,xx,yy,maxdep); //如果有可能的话,接着往下搜
swap(mp[x][y],mp[xx][yy]); //别忘了回溯
}
}
signed main(){
T=read();
while(T--){
success=0;
flag=0; //别忘了先清零
for(register int i(1) ; i<=5 ; i=-~i){
for(register int j(1) ; j<=5 ; j=-~j){
char ch;
cin >> ch;
if(ch == '*') mp[i][j] = 2,sx=i,sy=j; //这道题枚举那个空白好,因为次数少
else mp[i][j] = ch-'0';
}
}
if(!evaluate()) {printf("0\n");continue;}
for(register int maxdep(1) ; maxdep<=15 ; maxdep=-~maxdep){ //限制最多次数
A_star(0,sx,sy,maxdep);
if(success) {printf("%d\n",maxdep);flag=1;break;} //如果能匹配
}
if(!flag) printf("-1\n"); //不能匹配
}
return 0;
}