zoukankan      html  css  js  c++  java
  • 关于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就不太好解决了。

  • 相关阅读:
    Mysql主从同步延迟问题及解决方案
    elasticsearch 查询过程
    RPC(Remote Procedure Call):远程过程调用
    windows
    设计模式
    Linux Safe
    AS
    开机启动
    springboot打包部署
    【Linux】Linux 常用命令汇总
  • 原文地址:https://www.cnblogs.com/fujudge/p/7359039.html
Copyright © 2011-2022 走看看