• 【算法总结】广搜


    算法总结-广搜(BFS:breadth-first search

    广度优先搜索算法(用QUEUE)

    1. 把初始节点S0放入Open表(待扩展表)中;
    2. 如果Open表为空,则问题无解,失败退出;
    3. 把Open表的第一个节点取出放入Closed表,并记该节点为n;
    4. 考察节点n是否为目标节点。若是,则得到问题的解,成功退出;
    5. 若节点n不可拓展,则转第2步;
    6. 扩展节点n,将其不在Closed表和Open表的子节点(判重)放入Open表的尾部,并为每一个子节点设置指向父节点的指针(或记录节点的层次),然后转第2步;

    (详见刘家瑛课程PPT,结合Catch That Cow)

    广搜和深搜的比较

    广搜一般用于状态表示比较简单、求最优策略的问题。一层一层的搜,每一条路径的搜索进度都是一样的,因此需要用到队列的知识,不需要使用递归.

    优点:是一种完备策略,即只要问题有解,它就一定可以找到解 。并且,广度优先搜索找到的解,还一定是路径最短的解

    缺点:盲目性较大,尤其是当目标节点距初始节点较远时,将产生许多无用的节点,因此其搜索效率较低。需要保存所有扩展出 的状态,占用的空间大

    深搜几乎可以用于任何问题,一条路搜到底,不能再往下了,就回溯一步,需要用到递归,且只需要保存从起始状态到当前状态路径上的节点

    因此dfs适合那些求可行性路径数目的,而bfs适合求最短路径之类的(因为一旦搜到,就是最短)

    ——根据题目要求凭借自己的经验和对两个搜索的熟练程度做出选择 。

    数据结构:使用队列queue完成bfs,通常需要点结构来存储结点信息

    bool vis[5][5];//记录访问数组
    queue <Node> q;
    struct Node {
        int x;
        int y;
    };

    bfs():广度遍历地图

    Node bfs()//返回终点
    {
        queue <Node> q;
        Node cur, next;
        cur.x = 0;
        cur.y = 0;//初始结点信息
        q.push(cur);//初始结点入队,
        while (!q.empty()) //开启bfs过程
        {
            cur = q.front();//取队头结点
            q.pop();
            if (cur.x == 终点.x && cur.y == 终点.y) return cur;//过程结束条件
            int i, nx, ny;//新的待遍历结点
            for (i = 0; i < 4; i++) {
                nx = cur.x + dx[i];
                ny = cur.y + dy[i];
                if (judge(nx, ny))continue; //不可以走,边界或者已遍历等
                next = cur;
                next.x = nx;
                next.y = ny;
                q.push(next);//新结点入队
            }
        }
    }

    广搜例题-poj3278 Catch That Cow

    #include<iostream>
    #include<cstring>
    #include<queue>
    
    using namespace std;
    
    int N, K;//农夫起点和牛的位置
    const int MAXN = 100000;//最大坐标范围
    int visited[MAXN + 10];//判重标记,visited[i]=true表示i已经扩展过
    struct Step//一个坐标
    {
        int x;//位置
        int steps;//达到x所需的步数,即找到牛所需要的时间
        Step(int xx,int s):x(xx),steps(s){}//构造函数
    };
    
    queue<Step> q;//队列,即Open表
    
    int main()
    {
        cin >> N >> K;
        memset(visited, 0, sizeof(visited));
        q.push(Step(N, 0));//将农夫起点加入扩展队列
        visited[N] = 1;//标记N,起点位置已访问
        while (!q.empty())//Open表如果不为空,则一直扩展,直到Open表为空或者扩展到目标结点
        {
            Step s = q.front();//读取队头元素
            if (s.x == K)//找到目标
            {
                cout << s.steps << endl;
                return 0;//直接结束
            }
            else
            {
                if (s.x - 1 >= 0 && !visited[s.x - 1])//能往左边走且左边的结点还没有被访问过
                {
                    q.push(Step(s.x - 1, s.steps + 1));
                    visited[s.x - 1] = 1;
                }
                if (s.x + 1 <= MAXN && !visited[s.x + 1])//能往右边走且右边的结点还没有被访问过
                {
                    q.push(Step(s.x + 1, s.steps + 1));
                    visited[s.x + 1] = 1;
                }
                if (s.x * 2 <= MAXN && !visited[s.x * 2])//能往左边走一倍且左边的结点还没有被访问过
                {
                    q.push(Step(s.x * 2, s.steps + 1));
                    visited[s.x * 2] = 1;
                }
                q.pop();//队头元素出列
            }
        }
        return 0;
    }
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    int vis[100005] = { 0 };
    struct Node
    {
        int x, t;
    };
    
    queue<Node>q;
    
    bool valid(int x)
    {
        return (x >= 0 && x <= 100000);
    }
    
    int main()
    {
        int n, k;
        scanf("%d%d", &n, &k);
        Node s;
        s.x = n;
        s.t = 0;
        vis[n] = 1;
        q.push(s);
        while (!q.empty())
        {
            Node cur = q.front();
            q.pop();
            if (cur.x == k)
            {
                printf("%d", cur.t);
                return 0;
            }
            Node tmp;
            if (valid(cur.x + 1) && !vis[cur.x + 1])
            {
                tmp.x = cur.x + 1;
                vis[cur.x + 1] = 1;
                tmp.t = cur.t + 1;
                q.push(tmp);
            }
            if (valid(cur.x * 2) && !vis[cur.x * 2])
            {
                tmp.x = cur.x * 2;
                tmp.t = cur.t + 1;
                vis[cur.x * 2] = 1;
                q.push(tmp);
            }
            if (valid(cur.x - 1) && !vis[cur.x - 1])
            {
                tmp.x = cur.x - 1;
                vis[cur.x - 1] = 1;
                tmp.t = cur.t + 1;
                q.push(tmp);
            }
        }
        return 0;
    }
    二刷

    广搜模板(地图寻路)

    struct st{
        ...
    };
    
    queue<st> q;
    
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    
    void bfs(){
        q.push(start_st);
        while(!q.empty()){
            st tmp=q.front();
            q.pop();
            for(int i = 0; i < 4;++i){
                if(...)
                    q.push(st(...));
            }
        }
    }

    寻路例题 POJ 3984 迷宫问题

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <queue>
    using namespace std;
    
    bool vis[5][5];
    int a[5][5];
    int dx[4] = {0,1,0,-1};
    int dy[4] = {1,0,-1,0};
    
    struct Node{
        int x;
        int y;
        int s;
        short l[30];
    };
    
    bool Noway(int x,int y)
    {
        if(x<0 || x>=5 || y<0 || y>=5) return true;
        if(vis[x][y]) return true;
        if(a[x][y]==1) return true;
        return false;
    }
    
    Node bfs()
    {
        queue <Node> q;
        Node cur,next;
        cur.x = 0;
        cur.y = 0;
        cur.s = 0;
        vis[cur.x][cur.y] = true;
        q.push(cur);
        while(!q.empty()){
            cur = q.front();
            q.pop();
            if(cur.x==4 && cur.y==4)
                return cur;
            int i,nx,ny;
            for(i=0;i<4;i++){
                nx = cur.x + dx[i];
                ny = cur.y + dy[i];
                if(Noway(nx,ny))continue;//不可以走
                vis[nx][ny] = true;
                next = cur;
                next.x = nx;
                next.y = ny;
                next.s = cur.s + 1;
                next.l[cur.s] = i; 
                q.push(next); 
            } 
        }
        return cur;
    }
    
    
    int main()
    {
        int i,j;
        for(i=0;i<5;i++){   //读入迷宫
            for(j=0;j<5;j++){
                scanf("%d",&a[i][j]);
            }
        }
        memset(vis,0,sizeof(vis));
        Node ans = bfs();
        int x,y;
        x = 0,y = 0;
        for(i=0;i<=ans.s;i++){
             printf("(%d, %d)
    ",x,y);
             x+=dx[ans.l[i]];
             y+=dy[ans.l[i]];
        }        
        return 0;
    }

    双向广搜(DFBS)

    定义:从两个方向以广度优先的顺序同时扩展。

    比较:

    • DBFS算法相对于BFS算法来说,由于采用了双向扩展的方式,搜索树的宽度得到了明显的减少,时间复杂度和空间复杂度上都有提高! 
    • 假设1个结点能扩展出n个结点,单向搜索要m层能找到答案,那么扩展出来的节点数目就是: (1-nm)/(1-n) 。
    • 双向广搜,同样是一共扩展m层,假定两边各扩展出m/2层,则总结点数目 2 * (1-nm/2)/(1-n)。
    • 每次扩展结点总是选择结点比较少的那边进行扩展,并不是机械的两边交替。 

     框架:

    一、双向广搜函数:

    void dbfs() {

    1. 将起始节点放入队列q0,将目标节点放入队列q1
    2. 当两个队列都未空时,作如下循环:如果队列q0未空,不断扩展q0直到为空;
      1. 如果队列q0里的节点比q1中的少,则扩展队列q0
      2. 否则扩展队列q1
    3. 如果队列q0未空,不断扩展q0直到为空;
    4. 如果队列q1未空,不断扩展q1直到为空;

    }

    二、扩展函数:

    int expand(i) //其中i为队列的编号,0或1 

    {           

    • 取队列qi的头结点H;           
    • 对H的每一个相邻节点adj: 

                       1.如果adj已经在队列qi之中出现过,则抛弃adj; 

                       2.如果adj在队列qi中未出现过,则: 

                                 1) 将adj放入队列qi;                     

                                 2) 如果adj 曾在队列q1-i中出现过, 则:输出找到的路径

    }   

    需要两个标志序列,分别记录节点是否出现在两个队列中

  • 相关阅读:
    比较两个DataTable数据(结构相同),返回新增的,删除的,修改前的,修改后的 DataTable
    通用jig类,用到委托
    网站性能优化之HTTP请求过程简述!
    常见JS效果实现实现之图片减速度滚动
    Firefox中实现的outerHTML
    电子商务网站上的常用的放大镜效果
    div背景半透明,覆盖整个可视区域的遮罩层效果
    Javascript类定义语法,私有成员、受保护成员、静态成员等
    HTML/CSS控制div垂直&&水平居中屏幕
    CSS团队协作开发方式的思考
  • 原文地址:https://www.cnblogs.com/yun-an/p/11048950.html
Copyright © 2020-2023  润新知