关于dbfs原理 相对于bfs的复杂度优化及应用

(关于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就不太好解决了。

原文地址:https://www.cnblogs.com/fujudge/p/7359039.html