广度搜索bfs

    广度搜索在acm中应用十分广泛,英文简写是BFS(breadth first search)。

下面先看一下例子:

在一个4*4的矩形中,有一些位置设置有障碍,要求从(1,1)走到(4,4),求最短距离。

分析:假设没有任何障碍,我们可以走的路线如下:

起点为(1,1),假设这一步是第一步可以到达的位置;然后它可以向相邻的方向走一步,

如向右或向下就到达2号位置,2号就代表从起点到这个位置要走两步;3又是2号走一步;

4是3走一步;这样子就像从1号展开以水波,向四周扩散,我们只要把这些相邻的位置

全部保存在队列中,就会遍历完相邻的区域。

下面通过一个具体的例子讲一下如何编程实现:

问题:

南阳理工学院校园里有一些小河和一些湖泊,现在,我们把它们通一看成水池,假设有一张我们学校的某处的地图,这个地图上仅标识了此处是否是水池,现在,你的任务来了,请用计算机算出该地图中共有几个水池。

输入:

第一行输入一个整数N,表示共有N组测试数据
每一组数据都是先输入该地图的行数m(0<m<100)与列数n(0<n<100),然后,输入接下来的m行每行输入n个数,表示此处有水还是没水(1表示此处是水池,0表示此处是地面)

输出:

输出该地图中水池的个数。
要注意,每个水池的旁边(上下左右四个位置)如果还是水池的话的话,它们可以看做是同一个水池。

下面是程序的源代码:

#include<stdio.h>
#include<iostream>
#include<queue>

using namespace std;

struct Point
{
    int x;
    int y;
};

int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//分别表示向四个方向做一步
int main()
{
    queue<Point> que;//保存相连的一片水池
    Point p1,p2;
    int cnt;
    scanf("%d",&cnt);
    while(cnt--)
    {
        int m,n;
        scanf("%d %d",&m,&n);
        bool check[101][101]={false};//全部没有访问过
        int map[100][100];
        int i,j;
        int count=0;//水池个数
        for(i=0;i<m;i++)
            for(j=0;j<n;j++)
            {
                scanf("%d",&map[i][j]);
            }//for(j)
        for(i=0;i<m;i++)
            for(j=0;j<n;j++)
            {
                if(map[i][j]==1&&check[i][j]==false)//遇到一个新的水池
                {
                    p1.x=i;
                    p1.y=j;
                    que.push(p1);//当前点如队列
                    ++count;//发现一个就加1
                    check[p1.x][p1.y]=true;
                    while(que.empty()==false)  //把相连的全部如对列,只要队列不空,继续找相连的
                    {
                        p1=que.front();//取出头
                        que.pop();//每次都要弹出,但是不一定会有进去的,所以会结束

                        int k;
                        for(k=0;k<4;k++)  //四个方向
                        {
                            p2.x=p1.x+dir[k][0];
                            p2.y=p1.y+dir[k][1];

                            if(p2.x<0||p2.x>=m||p2.y<0||p2.y>=n)//超出范围
                            {
                                continue;
                            }
                            if(map[p2.x][p2.y]==1&&check[p2.x][p2.y]==false)
                            {
                                check[p2.x][p2.y]=true;
                                que.push(p2);
                            }
                        }//for
                    }//while
                    
                }//if


            }//for(i)
            cout<<count<<endl;
    }
    
    return 0;
}

注意:

1:遍历时要把相邻的水池算成一个,那么就要向四个方向同步搜索,设置一个dir[4][2]数组,

分别加上这个数组的值,就代表了向四个方向的遍历。

2:广度搜索的思想就是从一个地方向四周搜,我们把这个中心放到队列里,然后把所有相邻的

也同时加到队列里,并且访问过的做个标记(check[i][j]=true),这样就能确保全部遍历。

问题2:亡命逃窜  http://acm.nyist.net/JudgeOnline/problem.php?pid=523


下面是源程序:

#include<stdio.h>
#include<queue>

using namespace std;
struct unit
{
    int step;
    bool check;
};

unit maze[51][51][51];//存放路径
struct point
{
    int x;
    int y;
    int z;
};

int dir[6][3]={{1,0,0},{0,1,0},{0,0,1},{-1,0,0},{0,-1,0},{0,0,-1}};//分别向六个方向搜索

int main()
{
    int i,j,k;
    int cnt;
    scanf("%d",&cnt);
    while(cnt--)
    {
        int a,b,c,t;
        scanf("%d %d %d %d",&a,&b,&c,&t);
        for(i=0;i<a;i++)
            for(j=0;j<b;j++)
                for(k=0;k<c;k++)
                {
                    scanf("%d",&maze[i][j][k].step);
                    maze[i][j][k].check=false;
                }

        bool reachdoor=false;
        bool outtime=false;
        
        point p1,p2;
        queue<point> que;
        //必然是一条连通的路,所以只要一个节点进入队列,就能遍历所有的节点
        int step=0;
        p1.x=p1.y=p1.z=0;
        maze[p1.x][p1.y][p1.z].step=0;//两个参数都在入队的时候设置;没有这一步就wrong
        maze[p1.x][p1.y][p1.z].check=true;
        que.push(p1);
        while(!que.empty()&&!reachdoor&&!outtime)
        {
            p1=que.front();
            que.pop();            
            step=maze[p1.x][p1.y][p1.z].step;
            if(step>t)
            {
                outtime=true;
                break;
            }
            if(p1.x==a-1&&p1.y==b-1&&p1.z==c-1)//到门口
            {
                reachdoor=true;
                break;
            }
            int m;
            for(m=0;m<6;m++)
            {
                p2.x=p1.x+dir[m][0];
                p2.y=p1.y+dir[m][1];
                p2.z=p1.z+dir[m][2];
                if(p2.x<0||p2.y<0||p2.z<0||p2.x>=a||p2.y>=b||p2.z>=c)
                {
                    continue;
                }//if
                
                if(!maze[p2.x][p2.y][p2.z].check&&maze[p2.x][p2.y][p2.z].step==0)//没有检查且是路
                {
                    maze[p2.x][p2.y][p2.z].check=true;
                    maze[p2.x][p2.y][p2.z].step=step+1;                    
                    que.push(p2);
                }
            }//for(m)            
        }//while(que)
        if(reachdoor&&!outtime)
            printf("%d\n",step);
        else
            printf("-1\n");        
    }//while(cnt--)
    return 0;
}

分析:

(1)这个题编程立体形状;需要向六个方向遍历。

(2)不是像水池一样,这题求最短路径,因为有时间限制,所以只需要把一个点入队列,就可以

遍历所有的节点。

(3)有关节点的信息,如check,step都要在push之前设置,要不就会出错。

(4)逻辑关系,如超时、到点终点、队列为空,都是循环结束的条件,把这些条件放到外面,

内层只需要根据条件等级break,或者push。

原文地址:https://www.cnblogs.com/wang7/p/2479095.html