一、迭代加深搜索
经典例题:埃及分数问题
在古埃及,人们用单位分数(即1/a,a是正整数)的和表示一切有理数。例如,2/3=1/2+1/6,但不允许2/3=1/3+1/3,因为在加数中不允许有相同的。
对于一个分数a/b,表示法有许多种,其中加数少的比加数多的好,如果加数个数相同,则最小的分数越大越好。
输入:
两个正整数 a,b(0<a<b<500)
输出:
表达式,详见样例
样例输入:
495 499
样例输出:
495/499=1/2+1/5+1/6+1/8+1/3992+1/14970
bfs(宽度优先搜索)可以求起始状态到目标状态所需的最少步数(图上起始点到目标点的最短路),因为bfs是先搜索完当前层的所以可能结点,在搜索下一层,一步一步向下寻找目标结点,搜索到目标结点时到达的深度是所有可能路径最短的长度。
当一些题的解答树每一层的结点非常多时,bfs就不行了。
例题如果用bfs连一层都遍历不完(每一层都是无限大的)
迭代加深搜索(Iterative Deepening):从小到大枚举深度上限maxd,每次dfs只搜索深度不超过maxd的结点。当某一次dfs达到目标结点时,此时的深度maxd就是最少步数。从小到大枚举深度上限跟bfs的思想一样,一层一层的向下搜索解答树。
int IDdfs(){
for(maxd=0;;maxd++){
if(dfs(0,...)) return maxd;
}
return -1;
}
二、乐观评估函数
设前 d 个分数之和为 c/d ,第 d 个分数为 1/e ,则接下来至少需要 h=(a/b-c/d)/(1/e) 个分数,总和才能达到 a/b ,如果 d+h>maxd 则在往下搜索是没有必要的了,因为深度肯定会超过上限maxd。
乐观评估函数 :跟具当前状态与目标状态之间的关系,得出的从当前状态到达目标状态至少需要多少步(距离)
设 g(x) 表示从起始状态到达状态 x 时已走的步数(当前结点深度),h(x) 表示从状态 x 到达目标状态至少需要的步数(乐观评估函数),则当g(x)+h(x)>maxd 时应该考虑剪枝
bool dfs(int d,...){
if(isok()) return true;
if(d+h()>maxd) return false;
for(){
if(dfs(d+1/,...)) return true;
}
}
乐观评估函数:估值是“至少”
- 小一点会增加时间复杂度
- 大一点会导致结果不对
- 为0(无评估函数)就是爆搜
三、IDA*
迭代加深搜索+评估函数剪枝
洛谷 P2324 [SCOI2005]骑士精神
题目描述:
输入格式:
第一行有一个正整数T(T<=10),表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。
输出格式:
对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。
样例输入:
2
10110
01*11
10111
01001
0000001011
110*1
01110
01010
00100
样例输出:
7
-1
分析:
dfs考虑移动空格而不是马,每次有最多有8种可能的移动
迭代加深搜索最大深度15
评估函数:当前局面与目标局面有多少格不同(除了空格位),则至少需要多少步才能到达目标局面
代码:
#include <iostream>
#include <string.h>
#define _for(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
string ta[5];
int dx[8]={-2,-2,2,2,-1,-1,1,1};//马的移动
int dy[8]={-1,1,-1,1,-2,2,-2,2};
string st[5]={"11111","01111","00*11","00001","00000"};//目标状态
int hv(){//评估函数
int ret=0;
_for(i,0,4)
_for(j,0,4) if(ta[i][j]!=st[i][j]) ret++;
if(ta[2][2]!='*') ret--;
return ret;
}
bool dfs(int d,int maxd,int x,int y,int h){//dfs
if(h==0) return true;
if(d+h>maxd) return false;
_for(i,0,7){
int xx=x+dx[i],yy=y+dy[i];
if(xx>=0&&xx<5&&yy>=0&&yy<5){
ta[x][y]^=ta[xx][yy]^=ta[x][y]^=ta[xx][yy];//将空格与马互换
if(dfs(d+1,maxd,xx,yy,hv())) return true;
ta[x][y]^=ta[xx][yy]^=ta[x][y]^=ta[xx][yy];//回溯
}
}
return false;
}
int IDAdfs(int x,int y){
int h=hv();
_for(maxd,0,15){//迭代加深搜索
if(dfs(0,maxd,x,y,h)) return maxd;
}
return -1;
}
int main(){
int t,x,y;
cin>>t;
while(t--){
_for(i,0,4) cin>>ta[i];
_for(i,0,4)
_for(j,0,4) if(ta[i][j]=='*'){
x=i;y=j;break;
}
cout<<IDAdfs(x,y)<<"\n";
}
return 0;
}