(关于dbfs,A*,迭代加深及idA*的学习借鉴于北大暑假ACM课)
dbfs即双向广搜,从起点终点同时bfs,直到两个bfs有了交点,就找到了答案。
dbfs对单向bfs在搜索广度上有明显的减少,在时间及空间复杂度上都有极大的优化。
举例:
假设1个结点能扩展出n个结点,单向搜索要m层能找到答案,那 么扩展出来的节点数目就是: (1-n^ m)/(1-n)
而dbfs节点数目为2 * (1-n^m/2)/(1-n)
通过八数码问题能极大的体现出双向广搜的优越性
poj1077 bfs 735ms 2892K dbfs 32ms 1040K
接下来通过一道例题模拟dbfs思路
当你站在一个迷宫里的时候,往往会被错综复杂的道路弄得失去方向感,如果你能
得到迷宫地图,事情就会变得非常简单。
假设你已经得到了一个 n × m 的迷宫的图纸,请你找出从起点到出口的最短路
第一行是两个整数 n 和 m,表示迷宫的行数和列数。
接下来 n 行,每行一个长为 m 的字符串,表示整个迷宫的布局。字符. 表示空地,
# 表示墙,S 表示起点,T 表示出口。
输出从起点到出口最少需要走的步数
#include<cstdio> #include<algorithm> #include<cstring> #define maxn 110 #include<queue> using namespace std; queue<int> q[2]; int u[5]={0,0,1,-1},p[5]={1,-1,0,0}; int n,m; int wall[maxn*maxn],vis[2][maxn*maxn],inque[2][maxn*maxn],dis[maxn*maxn]; char map[maxn][maxn]; int cal(int x,int y){ return x*m+y; }//由于使用队列,将二维转化一维 int ok(int x,int y)//判断越界问题 { if(x>=0&&y>=0&&x<n&&y<m)return true; return false; } int dbfs(int st,int ed) { memset(dis,0x7f,sizeof(dis)); dis[st]=0; dis[ed]=0; q[0].push(st);//将起点存入队列1,终点存入队列2 q[1].push(ed); inque[0][st]=1;//记录是否入队 inque[1][ed]=1; while(!q[1].empty()&&!q[0].empty())//两队都不为空 { int id=q[0].size()>q[1].size();//为使相遇点尽量靠近中间点,每次扩展队内元素少的一边 int d=q[id].front(); q[id].pop(); inque[id][d]=0;//出队 int x=d/m;//求其二维坐标 int y=d%m; for(int i = 0 ; i < 4 ; ++i) { int dx=x+u[i],dy=y+p[i]; int dd=cal(dx,dy); if(!ok(dx,dy)||wall[dd])continue; if(vis[id^1][dd]||inque[id^1][dd])return dis[d]+dis[dd]+1;//相遇点,返回dis if(dis[dd]>dis[d]+1)//松弛 { dis[dd]=dis[d]+1; if(!inque[id][dd]) { inque[id][dd]=1; q[id].push(dd); } } } vis[id][d]=1; //放入closed表内 } int id=q[0].empty();//找到队内不为空的一队 while(!q[id].empty()) { int d=q[id].front(); q[id].pop(); inque[id][d]=0; int x=d/m; int y=d%m; for(int i = 0 ; i < 4 ; ++i) { int dx=x+u[i],dy=y+p[i]; int dd=cal(dx,dy); if(!ok(dx,dy)||wall[dd])continue; if(vis[id^1][dd]||inque[id^1][dd])return dis[d]+dis[dd]+1; if(dis[dd]>dis[d]+1) { dis[dd]=dis[d]+1; if(!inque[id][dd]) { inque[id][dd]=1; q[id].push(dd); } } } vis[id][d]=1; } } int main() { //freopen("maze.in","r",stdin); //freopen("maze.out","w",stdout); int s,t; scanf("%d%d",&n,&m); for(int i = 0 ; i < n ; ++i) scanf("%s",map[i]); for(int i = 0 ; i < n ; ++i) for(int j = 0 ; j < m ; ++j) { if(map[i][j]=='S')s=cal(i,j); else if(map[i][j]=='T')t=cal(i,j); else if(map[i][j]=='#')wall[cal(i,j)]=1; } printf("%d",dbfs(s,t)); return 0; }
inque 相当于open表
vis 相当于 closed 表
双向广搜适用于大多数bfs可解决的问题,但还是要具体问题具体分析,比如 抓住那头牛 由于终点走向起点时可以走x/2,与正向遍历相反,用dbfs就不太好解决了。