广度搜索bfs

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

下面先看一下例子:

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

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

广度搜索bfs

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

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

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

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

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

问题:

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

输入:

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

输出:

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

广度搜索bfs

广度搜索bfs

下面是程序的源代码:

#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。

你可能感兴趣的:(bfs)