层层递进----------广度优先搜索

前言:

宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。

BFS,其英文全称是Breadth First Search。 BFS并不使用经验法则算法。从算法的观点,所有因为展开节点而得到的子节点都会被加进一个先进先出的队列中。每次的搜索都是把现在这个节点周围所有的节点放入一个队列中,在把这个节点搜索完后,就从队列中取出来一个(队列是先进先出,所以出来的一定是最先进去的数据),重复上面的操作,直至结束(找到结果提前结束,没有找到结果,队列为空)。

举个例子:

       对于上面的这个图,如果我们进行广度优先搜索,那么我们假设搜索的起点是根节点A,队列是Q{  },现在队列为空,那么开始进行搜索,与节点A相连的节点有节点B和节点C,(假设搜索的顺序是先左孩子,再右孩子),那么第一搜索到的节点就是B,入队列,Q{B},再然后搜索到的节点就是C,入队列,Q{B,C},然后我们可以发现,节点A下的所有节点都被搜索完了,那么我们要从队列中取出来一个节点作为搜索的节点开始搜索,那么我们会取出来节点B,Q{C},然后开始重复上述操作,搜索节点D,Q{C,D},搜索节点E,入队列Q{C,D,E},节点B搜索完毕,从队列中取出来节点C,发现节点C下没有可以搜索的节点,Q{D,E},那么就再取出来节点D,Q{E},发现节点D也没有可以搜索的节点,那么再取出来节点E,Q{},然后会搜索到节点F,G,入队列Q{F,G},然后取出来节点F,没有可以搜索的节点,Q{G},取出来节点G,Q{},发现没有可以搜索的节点,然后再去队列中取一个节点,但是发现队列中没有可以去出来的节点,那么搜索结束。

        看到上面的搜索过程我们可以发现,广度优先搜索是一层一层的搜索整个图,是层层递进的。当然上面的搜索过程是对于树进行的搜索,当我们对于矩阵进行搜索就有不同了,因为一个点的邻接点有可能也是别的点的邻接点,那么前面已经进入队列中了,后面就不用再进入队列了,所以我们要用一个数组来标记点入队列的情况,即这个点是否被搜索过。

下面找几道广度优先搜索的题目来加深理解吧。

Dungeon Master

 POJ - 2251 

题目:

You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is composed of unit cubes which may or may not be filled with rock. It takes one minute to move one unit north, south, east, west, up or down. You cannot move diagonally and the maze is surrounded by solid rock on all sides. 

Is an escape possible? If yes, how long will it take? 

Input

The input consists of a number of dungeons. Each dungeon description starts with a line containing three integers L, R and C (all limited to 30 in size). 
L is the number of levels making up the dungeon. 
R and C are the number of rows and columns making up the plan of each level. 
Then there will follow L blocks of R lines each containing C characters. Each character describes one cell of the dungeon. A cell full of rock is indicated by a '#' and empty cells are represented by a '.'. Your starting position is indicated by 'S' and the exit by the letter 'E'. There's a single blank line after each level. Input is terminated by three zeroes for L, R and C.

Output

Each maze generates one line of output. If it is possible to reach the exit, print a line of the form 

Escaped in x minute(s).


where x is replaced by the shortest time it takes to escape. 
If it is not possible to escape, print the line 

Trapped!

Sample Input

3 4 5
S....
.###.
.##..
###.#

#####
#####
##.##
##...

#####
#####
#.###
####E

1 3 3
S##
#E#
###

0 0 0

Sample Output

Escaped in 11 minute(s).
Trapped!

题意:

先给你三个数字,代表地牢的长 宽 高,S代表被困的地方,E代表出口,每次只能上 下 左 右 前 后 移动一步,# 代表障碍物,. 代表道路,能否逃出地牢,如果能逃出,求最短的逃出时间,输出逃出时间,否则输出 被困。

思路:

这道题就是一个经典的广度优先搜索的模板题,这道题是对一个三维数组进行搜索,只是简单的套模板,下面看看代码,试试上面的搜索思路如何转换成代码。

代码如下:

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
 
char map[50][50][50];//存储三位数组的地图;
int book[50][50][50];//用来标记点是否被搜索过;
int k,n,m;//地图的长宽高;
int sx,sy,sz,ex,ey,ez;//记录起点和终点;
int nextt[6][3]={{0,0,1},{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}};//搜索的6个方向,因为每一次只能走一步;
 
struct node//一个结构体,用来记录现在的位置和所走的步数;
{
    int x;
    int y;
    int z;
    int step;//步数;
};
 
int bfs()
{
    int i;
    node a,next;
    queue<node>Q;//定义一个队列,是结构体的类型;
    a.x=sx;//结构体a存储的是搜索开始的起点;
    a.y=sy;
    a.z=sz;
    a.step=0;//现在的步数是0;
    book[a.x][a.y][a.z]=1;//把这个点标记成搜索过;
    Q.push(a);//入队列;
    while(!Q.empty())//如果队列中不为空;如果队列中为空就结束搜索;
    {
        a=Q.front();//从队列中取出来首元素;
        Q.pop();
        if(a.x==ex&&a.y==ey&&a.z==ez)//到达出口,返回最少的步数;
            return a.step;
        for(i=0;i<6;i++)//对6个方向进行搜索;
        {
            next.x=a.x+nextt[i][0];
            next.y=a.y+nextt[i][1];
            next.z=a.z+nextt[i][2];
            if(next.x<0||next.x>=k||next.y<0||next.y>=n||next.z<0||next.z>=m)
                continue;//出界,continue;
            if(book[next.x][next.y][next.z]==1||map[next.x][next.y][next.z]=='#')
                continue;//遇到障碍物,或者这条路被走过,continue;
            book[next.x][next.y][next.z]=1;//否者把这条路标记为走过;
            next.step=a.step+1;//步数+1;
            Q.push(next);//把这个位置入队列;
        }
    }
    return 0;//没有搜到结果,那么返回0;
}
 
int main()
{
    while(~scanf("%d%d%d",&k,&n,&m))
    {
        if(k==0&&n==0&&m==0)//长宽高同时为0,结束输入;
            break;
        int i,j,r;
        for(i=0;i<k;i++)//存入地图;
        {
            for(j=0;j<n;j++)
            {
                scanf("%s",&map[i][j]);
                for(r=0;r<m;r++)
                {
                    if(map[i][j][r]=='S')//记下起点;
                    {
                        sx=i;
                        sy=j;
                        sz=r;
                    }
                    if(map[i][j][r]=='E')//记下终点;
                    {
                        ex=i;
                        ey=j;
                        ez=r;
                    }
                }
            }
        }
        memset(book,0,sizeof book);//标记数组初始化;
        int kk=bfs();//返回的步数;
        if(kk==0)//没有找到出口,被困;
            printf("Trapped!\n");
        else
        printf("Escaped in %d minute(s).\n",kk);//逃脱,输出步数;
    }
    return 0;
}

题目:

算法提高 学霸的迷宫

链接:http://lx.lanqiao.cn/problem.page?gpid=T291

题目:

  学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。

输入格式

  第一行两个整数n, m,为迷宫的长宽。
  接下来n行,每行m个数,数之间没有间隔,为0或1中的一个。0表示这个格子可以通过,1表示不可以。假设你现在已经在迷宫坐标(1,1)的地方,即左上角,迷宫的出口在(n,m)。每次移动时只能向上下左右4个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证(1,1),(n,m)可以通过。

输出格式

  第一行一个数为需要的最少步数K。
  第二行K个字符,每个字符∈{U,D,L,R},分别表示上下左右。如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。

样例输入

Input Sample 1:
3 3
001
100
110

Input Sample 2:
3 3
000
000
000

样例输出

Output Sample 1:
4
RDRD

Output Sample 2:
4
DDRR

数据规模和约定

  有20%的数据满足:1<=n,m<=10
  有50%的数据满足:1<=n,m<=50
  有100%的数据满足:1<=n,m<=500。

思路:

这道题和上面的广度优先搜索的问题一样是一道简单的搜索模板题,这道题是一个二维数组的搜索题,但是这道题有一点点的不同,就是题目中对于搜索方向的前后顺序有所要求(如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。),这也很简单,这要我们在搜索的时候按照要求的先后顺序进行搜索就可以得到想要的结果。还要注意,这道题要求输出路径,那么我们需要对广度优先搜索的模板进行修改,对于路径的记录我们只需要在结构体中加入一个string就可以进行历经的记录,那么,下面就去看看代码是怎么实现的吧。

2019年的河南蓝桥杯省赛出现了一道填空题,和这道题是同一个类型的题目,只不过是地图数据给你了,是一个30*50的地图,让你找出来路径最短且字典序最小的路径,输出路径。

代码如下:

#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
 
int n,m;
char map[510][510];//存储地图;
int book[510][510];//标记是否搜索过;
int nextt[4][2]= {{1,0},{0,-1},{0,1},{-1,0}};//这个要注意,题目要求路径一样的按照字典序小的输出;
                                                //按照字典序的顺序依次搜索    下,左,右,上;
char k[4]={'D','L','R','U'};
 
struct node
{
    int x;
    int y;
    int step;
    string s;//记录路径;
};
 
void bfs()
{
    int i;
    node a,b;
    queue<node>Q;//队列;
    memset(book,0,sizeof book);//清零;
    book[0][0]=1;//起点白哦及成搜索过;
    a.x=0;
    a.y=0;
    a.step=0;
    a.s="";//记得初始化;
    Q.push(a);//入队列;
    while(!Q.empty())//队列不为空;
    {
        a=Q.front();//取队列的首元素;
        Q.pop();
        if(a.x==n-1&&a.y==m-1)//到了出口就结束,输出路径;
        {
            printf("%d\n",a.step);
            cout<<a.s<<endl;//输出路径;
            return ;
        }
        for(i=0; i<4; i++)//四个方向进行搜索;
        {
            b.x=a.x+nextt[i][0];
            b.y=a.y+nextt[i][1];
            b.step=a.step+1;
            if(b.x<0||b.x>=n||b.y<0||b.y>=m)//判断边界;
                continue;
            if(book[b.x][b.y]==1||map[b.x][b.y]=='1')//判断是否搜索过,是否不能走;
                continue;
            b.s=a.s+k[i];//记录路径;
            book[b.x][b.y]=1;//标记搜索过,忘记会超时;
            Q.push(b);//进队列;
        }
    }
    return ;
}
 
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<n; i++)
        {
            scanf("%s",&map[i]);//字符串输人;
        }
        bfs();
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值