关于宽搜BFS广度优先搜索的那点事

  以前一直知道深搜是一个递归栈,广搜是队列,FIFO先进先出LILO后进后出啥的。DFS是以深度作为第一关键词,即当碰到岔道口时总是先选择其中的一条岔路前进,而不管其他岔路,直到碰到死胡同时才返回岔道口并选择其他岔路。接下来将介绍的广度优先搜索(Breadth First Search, BFS)则是以广度为第一关键词,当碰到岔道口时,总是先一次访问从该岔道口能直接到达的所有节结点,然后再按这些结点被访问的顺序去依次访问它们能直接到达的所有结点,以此类推,直到所有结点都被访问为止。这就跟平静的水面中投入一颗小石子一样,水花总是以石子落水处为中心,并以同心圆的方式向外扩散至整个水面,从这点来看BFS和DFS那种沿着一条线前进的思路是完全不同的。

  广度优先搜索BFS一般由队列实现,且总是按层次的顺序来进行遍历,其基本写法如下(可作模板用):

void BFS(int s)
{
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        取出队首元素top;
        访问队首元素top;
        将队首元素出队;
        将top的下一层结点中未曾入队的结点全部入队,并设置为已入队;
    }
}

(1)定义队列q,并将起点s入队。

(2)写一个while循环,循环条件是队列q非空。

(3)在while循环中,先取出队首元素top,然后访问它(访问可以是任何事情,例如将其输出)。访问完后将其出队。

(4)将top的下一层结点中所有未曾入队的结点入队,并标记它们的层号为now的层号+1,同时设置这些入队的结点已入过队。

(5)返回(2)继续循环。

  例1:给出一个mxn的矩阵,矩阵中的元素为0或1。称位置(x,y)与其上下左右四个位置(x,y+1)、(x,y-1)、(x+1,y)、(x-1,y)是相邻的。如果矩阵中有若干个1是相邻的(不必两两相邻),那么称这些1构成了一个“块”。求给定的矩阵中“块”的个数。

【输入样例】

6 7
0 1 1 1 0 0 1
0 0 1 0 0 0 0
0 0 0 0 1 0 0
0 0 0 1 1 1 0
1 1 1 0 1 0 0
1 1 1 1 0 0 0

【输出样例】

4

【解题思路】

  求解的基本思想是:枚举每一个位置的元素,如果为0,则跳过;如果为1,则使用BFS查询与该位置相邻的4个位置(前提是不出界),判段它们是否为1(如果某个相邻的位置为1,则同样去查询与该位置相邻的4个位置,直到整个“1”块访问完毕)。而为了防止走回头路,一般可以设置一个bool型数组inq来记录每个位置是否在BFS中已入过队。

  对当前位置(x,y)来说,由于与其相邻的四个位置分别为(x,y+1)、(x,y-1)、(x+1,y)、(x-1,y),那么不妨设置两个增量数组,来表示四个方向。

  int X[4] = {0,0,1,-1};

  int Y[4] = {1,-1,0,0};

  这样就可以使用for循环来枚举4个方向,以确定与当前坐标(nowX, nowY)相邻的4个位置,如下所示:

  for(int i = 0;i<4;i++)

  {

    newX = nowX + X[i];

    newY = nowY + Y[i];

  }

#include <bits/stdc++.h>
#include <queue>
using namespace std;
const int maxn = 100;
struct node {
    int x,y; //位置(x,y)
}Node;
int n,m; //矩阵大小为n*m
int matrix[maxn][maxn]; //01矩阵
bool inq[maxn][maxn]; //记录位置(x,y)是否已入过队
int X[4] = {0,0,1,-1}; //增量数组
int Y[4] = {1,-1,0,0};

bool judge(int x,int y) //判段坐标(x,y)是否需要访问
{
    //越界返回false
    if(x>=n || x<0 || y>=m || y<0)
        return false;
    //当前位置为0,或(x,y)已入过队,返回false
    if(matrix[x][y] == 0 || inq[x][y] == true) return false;

    //以上都不满足,说明需要访问,返回true
    return true;
}
//BFS函数方位位置(x,y)所在的块,将该块中所有“1”的inq都设置为true
void bfs(int x, int y)
{
    queue<node>Q; //定义队列
    Node.x = x, Node.y = y; //当前结点的坐标为(x,y)
    Q.push(Node); //将结点Node入队
    inq[x][y] = true; //设置(x,y)已入过队
    while(!Q.empty())
    {
        node top = Q.front(); //取出队首元素
        Q.pop(); //队首元素出队
        for(int i = 0;i<4;i++) //循环4次,得到4个相邻位置
        {
            int newX = top.x + X[i];
            int newY = top.y + Y[i];
            if(judge(newX,newY)) //如果新位置(newX,newY)需要访问
            {
                //设置Node的坐标为(newX,newY)
                Node.x = newX, Node.y = newY;
                Q.push(Node); //将结点Node加入队列
                inq[newX][newY] = true; //设置位置(newX,newY)已入过队
            }
        }
    }
}

int main()
{
    freopen("1.txt","r",stdin); //从文件读入
    cin>>n>>m;
    for(int x = 0;x<n;x++)
        for(int y = 0;y<m;y++)
            cin>>matrix[x][y]; //读入01矩阵

    int ans = 0; //存放块数
    for(int x= 0;x<n;x++) //枚举每一个位置
        for(int y = 0;y<m;y++)
            //如果元素为1,且未入过队
            if(matrix[x][y] == 1 && inq[x][y] == false)
            {
                ans++; //块数加1
                bfs(x,y); //访问整个块,将该块所有“1”的inq都标记为true
            }
    cout<<ans<<endl;

    return 0;
}

例2

5 5
.....
.*.*.
.*S*.
.***.
...T*
2 2 4 3

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100;
struct node
{
    int x,y; //位置(x,y)
    int step;//step为从起点S到达该位置的最少步数(即层数)
}S,T,Node; //S为起点,T为终点,Node为临时结点
int n,m; //n行m列
char maze[maxn][maxn]; //迷宫信息,字符数组
bool inq[maxn][maxn] = {false}; //记录(x,y)是否已入过队
int X[4] = {0,0,1,-1}; //增量数组
int Y[4] = {1,-1,0,0};

//检测位置(x,y)是否有效
bool test(int x,int y)
{
    if(x>=n || x < 0 || y >= m || y < 0) return false; //超过边界
    if(maze[x][y] == ‘*‘) return false; //墙壁*
    if(inq[x][y] == true) return false; //已入过队
    return true; //有效位置
}

int BFS()
{
    queue<node> q; //定义队列
    q.push(S); //将起点S入队
    while(!q.empty())
    {
        node top = q.front(); //取出队首元素
        q.pop(); //队首元素出队
        if(top.x == T.x && top.y == T.y)
        {
            return top.step; //终点,直接返回最小步数
        }
        for(int i = 0;i<4;i++) //循环4次,得到4个相邻位置
        {
            int newX = top.x + X[i];
            int newY = top.y + Y[i];
            if(test(newX,newY)) //位置(newX,newY)有效
            {
                Node.x = newX;
                Node.y = newY;
                Node.step = top.step + 1; //Node层数为top层数加1
                q.push(Node);    //将结点Node加入队列
                inq[newX][newY] = true;   //设置位置(newX,newY)已入过队
            }
        }
    }

    return -1; //无法到达终点T时返回-1
}

int main()
{
    freopen("2.txt","r",stdin);
    cin>>n>>m;
    for(int i = 0;i<n;i++)
    {
        for(int j = 0;j<m;j++)
            cin>>maze[i][j];
    }
    cin>>S.x>>S.y>>T.x>>T.y;//起点和终点的坐标
    S.step = 0; //初始化的起点的层数为0,S到T的最少步数

    cout<<BFS()<<endl;
    return 0;
}

例3

#include <bits/stdc++.h>
using namespace std;
struct node
{
    int x,y,z;
}Node;
int n,m,L,T;   //矩阵为n*m,共有L层,T为1的个数的下限
int pixel[1290][130][61];   //三位01矩阵
bool inq [1290][130][61] = {false};
int X[6] = {0,0,0,0,1,-1};   //增量矩阵
int Y[6] = {0,0,1,-1,0,0};
int Z[6] = {1,-1,0,0,0,0};
bool judge(int x,int y,int z)
{
    if(x>=n||x<0||y>=m||y<0||z>=L||z<0)
        return false;
    if(pixel[x][y][z] == 0 || inq[x][y][z] == true) return false;
    return true;
}
int BFS(int x,int y,int z)
{
    int tot = 0; //计数当前块中1的个数
    queue<node> Q;   //定义队列
    Node.x = x,Node.y = y,Node.z = z;
    Q.push(Node); //将结点Node入队
    inq[x][y][z] = true; //设置位置(x,y,z)已入过队
    while(!Q.empty())
    {
        node top = Q.front(); //取出队首元素
        Q.pop();
        tot++;
        for(int i = 0;i<6;i++)
        {
            int newX = top.x + X[i];
            int newY = top.y + Y[i];
            int newZ = top.z + Z[i];
            if(judge(newX, newY,newZ))
            {
                Node.x = newX,Node.y=newY,Node.z = newZ;
                Q.push(Node);
                inq[newX][newY][newZ]= true;
            }
        }

    }
    if(tot>=T) return tot;
    else return 0;
}

int main()
{
    freopen("3.txt","r",stdin);
    cin>>n>>m>>L>>T;
    for(int z = 0;z<L;z++)
        for(int x = 0;x <n;x++)
            for(int y = 0;y<m;y++)
                cin>>pixel[x][y][z];

    int ans = 0;   //记录卒中核心区1的个数总和
    for(int z =0;z<L;z++)
        for(int x = 0;x<n;x++)
            for(int y = 0;y<m;y++)
                if(pixel[x][y][z] == 1 && inq[x][y][z] == false)
                {
                    ans += BFS(x,y,z);
                }

    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/longxue1991/p/12025010.html

时间: 2024-08-28 18:14:59

关于宽搜BFS广度优先搜索的那点事的相关文章

GraphMatrix::BFS广度优先搜索

查找某一结点的邻居: virtual int firstNbr(int i) { return nextNbr(i, n); } //首个邻接顶点 virtual int nextNbr(int i, int j) //相对于顶点j的下一邻接顶点 { while ((-1 < j) && (!exists(i, --j))); return j; } //逆向线性试探(改用邻接表可提高效率) 对于图中的所有顶点,对每一个连通区域进行BFS: template <typename

步步为营(十六)搜索(二)BFS 广度优先搜索

上一篇讲了DFS,那么与之相应的就是BFS.也就是 宽度优先遍历,又称广度优先搜索算法. 首先,让我们回顾一下什么是"深度": 更学术点的说法,能够看做"单位距离下,离起始状态的长度" 那么广度是什么呢? 个人认为,能够这么归纳: 何为广度? 能够看做"距离初始状态距离相等的结点"的集合 那么BFS的核心思想就是:从初始结点開始,搜索生成第一层结点.检查目标结点是否在这些结点中,若没有,再将全部第一层的结点逐一进行搜索,得到第二层结点,并逐一检查

【LeetCode从零单排】No133. clon graph (BFS广度优先搜索)

背景 (以下背景资料转载自:http://www.cnblogs.com/springfor/p/3874591.html?utm_source=tuicool) DFS(Dpeth-first Search)顾名思义,就是深度搜索,一条路走到黑,再选新的路.记得上Algorithm的时候,教授举得例子就是说,DFS很像好奇的小孩,你给这个小孩几个盒子套盒子,好奇的小孩肯定会一个盒子打开后继续再在这个盒子里面搜索.等把这一套盒子都打开完,再打开第二套的.Wikipedia上的讲解是:"Depth

BFS - 广度优先搜索 - 邻接列表表示法

2017-07-25 21:40:22 writer:pprp 在DFS的基础上加上了一个BFS函数 #include <iostream> #include <queue> using namespace std; const int N = 9; queue<int> qu; int visited[N] = {0}; //新引入一个数组,用于标记是否访问过 struct node { int vertex; node*next; }; node head[N];

BFS(广度优先搜索)

///POJ 3278 Catch That Cow #include<stdio.h>#include<string.h>#include<iostream>#include<queue>using namespace std; int step[100010];int vis[100010];queue <int> q; int BFS(int n, int k){ int i; int head, next; q.push(n); step

邻接表实现BFS广度优先搜索

#include <stdio.h> #include <stdlib.h> #include <string.h> #define VERTEX_NUM 8 typedef enum {FALSE = 0, TRUE = 1}BOOL; typedef struct ArcNode { int adjvex; struct ArcNode *nextarc; // struct不能少 }ArcNode; BOOL visited[VERTEX_NUM + 1]; //

南阳理工--21--三个水杯~~广度优先搜索

这一题运用广度优先搜索可以解决,主要是各个状态的转移以及某个状态出现过要标记,避免重复,进入死循环. 下面是AC代码,上面有详细的讲解: # include <iostream> # include <cstring> # include <queue> using namespace std; class data //队列的结点, { public: int water[3]; //三个水杯的状态 int step; //步骤 }; bool visited[100

地牢逃脱(BFS(广度优先搜索))

题目描述 给定一个 n 行 m 列的地牢,其中 '.' 表示可以通行的位置,'X' 表示不可通行的障碍,牛牛从 (x0 , y0 ) 位置出发,遍历这个地牢,和一般的游戏所不同的是,他每一步只能按照一些指定的步长遍历地牢,要求每一步都不可以超过地牢的边界,也不能到达障碍上.地牢的出口可能在任意某个可以通行的位置上.牛牛想知道最坏情况下,他需要多少步才可以离开这个地牢. 输入描述: 每个输入包含 1 个测试用例.每个测试用例的第一行包含两个整数 n 和 m(1 <= n, m <= 50),表示

广度优先搜索(BFS)

广度优先 Description: 阿狸被困在迷宫,snoopy要去救他,snoopy可以向上.下.左.右四个方向行走,每走一步(格)就要喝掉一瓶益力多.现在给它一个迷宫地图请问:snoopy最少需要多少瓶益力多才能走出迷宫? Input: 先输入一个数t,表示测试的数据个数, 下面输入的就是t个迷宫, 每个迷宫的输入都应包含以下数据, 输入迷宫的大小 n(n<=15),表示迷宫大小为n*n. 再输入迷宫, 用大写字母“S”表示snoopy的位置, 用小写字母“E”表示阿狸被困的位置, 用“.”