C++ 算法篇 广度(宽度)优先搜索(BFS)

广度优先遍历

广度优先遍历(Breadth_First_Search),又称为广度优先搜索,简称BFS。

图的BFS类似于树的层序遍历。

C++ 算法篇 广度(宽度)优先搜索(BFS)_第1张图片

广度优先遍历

  • 如图将左边的图变形,得到右边的图,然后一层一层的遍历。
  • 这里借助一个队列来实现一层一层的遍历。

       搜索问题一般有两种情况:一种是给出初始结点,要求寻找符合约束条件的目标结点;另一种是给出初始结点和目标结点,要求找到从初始结点到目标结点的一条路径。对于解的要求也不尽相同,有的问题要求出一个可行解,有的则要求出最优解,还有的问题要找出全部解,有时还要按照一定的顺序输出。

    搜索问题中的数据结构一般要求表达要合理,有助于计算机的处理;信息要完整,能反应出状态的本质和状态之间的关系;还要节省存储空间,尽可能地提高搜索的速度。比如分油问题本来用一个三元组(x10,x7,x3)即可,但是为了输出方便,我们增加一个d,用来表示该结点的父结点(由谁拓展而来),即变成一个四元组(x10,x7,x3,d)。另外,还会用到队列来实现控制策略。当然,不能忘记状态变化过程中的重复性检查,比如哈希表,因为大多数情况下,出现重复状态是毫无意义的,会造成死循环和空间的浪费。

4. 宽度优先搜索的基本思想

宽度优先搜索(Breadth First Search,BFS),简称宽搜,又称广度优先搜索。它是从初始结点开始,应用产生式规则和控制策略生成第一层结点,同时检查目标结点是否在这些生成的结点中。若没有,再用产生式规则将所有第一层结点逐一拓展,得到第二层结点,并逐一检查第二层结点是否包含目标结点。若没有,再用产生式规则拓展第二层结点。如此依次拓展,检查下去,直至发现目标结点为止。如果拓展完所有结点,都没有发现目标结点,则问题无解。

C++ 算法篇 广度(宽度)优先搜索(BFS)_第2张图片

对于以上“无向图”,从顶点 V0 开始进行宽度优先搜索,得到的一个序列为 V0,V1,V2,V3,V4,V6,V5。

宽度优先搜索是一种“盲目”搜索,所有结点的拓展都遵循“先进先出”的原则,所以采用“队列”来存储这些状态。

宽度优先搜索的算法框架如下:

void BFS
{    while (front <= rear)  // 当队列非空时做,front 和 rear 分别表示队列的头指针和尾指针  
     {     if (找到目标状态)
              做相应处理(如退出循环输出解、输出当前解、比较解的优劣);
           else
           {   拓展头结点 ;
               if( 拓展出的新结点没出现过 )
               {   rear++;
                   将新结点插到队尾 ;
               }
           }
           front++;// 取下一个结点
      }
}

如果只要求任意一个解,也可以写成以下的结构:
void BFS2
{    p = true;
     while (p)
     {    if( 头结点是目标状态 ) p = false;
          else
          {   拓展头结点 ;
              if( 拓展出的新结点没出现过 )
              {    rear++;
                   将新结点插到队尾 ;
              }
              front++;
              if(front > rear)p = false;
           }
     }
}

图的DFS与BFS

  • 图的深度优先搜索算法和广度优先搜索算法在时间复杂度上是一样的。
  • 深度优先更适合目标比较明确,以找到目标为目的的情况。
  • 广度优先更适合在不断扩大遍历范围时找到相对最优解的情况

四、应用举例:

1、瓷砖

【问题描述】

在一个 w×h 的矩形广场上,每一块 1×1 的地面都铺设了红色或黑色的瓷砖。小林同学站在某一块黑色的瓷砖上,他可以从此处出发,移动到上、下、左、右四个相邻的且是黑色的瓷砖上。现在,他想知道,通过重复上述移动所能经过的黑色瓷砖数。

【输入格式】

第 1 行为 h、w,2≤w、h≤50,之间由一个空格隔开。  

以下为一个 w 行 h 列的二维字符矩阵,每个字符为“.”“#”“@”,分别表示该位置为黑色的瓷砖、红色的瓷砖,以及小林的初始位置。

【输出格式】

输出一行一个整数,表示小林从初始位置出发可以到达的瓷砖数。

【输入输出样例】

11 9

.#.........

.#.#######.

.#.#.....#.

.#.#.###.#.

.#.#..@#.#.

.#.#####.#.

.#.......#.

.#########.

...........

【问题分析】

本题是典型的“求连通块”问题,可以采用经典的“宽度优先搜索”算法求解,使用队列维护。

2、填涂颜色

题目描述

由数字0组成的方阵中,有一任意形状闭合圈,闭合圈由数字1构成,围圈时只走上下左右4个方向。现要求把闭合圈内的所有空间都填写成2.例如:6×6的方阵(n=6),涂色前和涂色后的方阵如下:

0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1

输入格式

每组测试数据第一行一个整数n(1≤n≤30)

接下来n行,由0和1组成的n×n的方阵。

方阵内只有一个闭合圈,圈内至少有一个0。

输出格式

已经填好数字2的完整方阵。

输入输出样例

输入 

6
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1

输出 

0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1

说明/提示

1≤n≤30

3、求细胞数量

题目描述

一矩形阵列由数字 0 到 9 组成,数字 1 到 9 代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。

输入格式

第一行两个整数代表矩阵大小 n 和 m。

接下来 n 行,每行一个长度为 m 的只含字符 0 到 9 的字符串,代表这个n×m 的矩阵。

输出格式

一行一个整数代表细胞个数。

输入输出样例

输入 

4 10
0234500067
1034560500
2045600671
0000000089

输出 

4

说明/提示

数据规模与约定

对于 100% 的数据,保证 1≤n,m≤100。

4、面积(area)

编程计算由“*”号围成的下列图形的面积。面积计算方法是统计*号所围成的闭合曲线中水平线和垂直线交点的数目。如下图所示,在10*10的二维数组中,有“*”围住了15个点,因此面积为15。

0  0  0  0  0  0  0  0  0  0

0  0  0  0   *  *  *  0  0  0

0  0  0  0   *  0  0  *  0  0

0  0  0  0  0   *  0  0  *  0

0  0  *  0  0  0   *  *  0

0   *  0  *  0  *  0  0  *  0

0   *  0  0  *  * *  *  0

0  0   *  0  0  0  0  *  0  0

0  0  0  *  *  *  *  *  0  0

0  0  0  0  0  0  0  0  0  0

【样例输入】area.in

0 0 0 0 0 0 0 0 0 0

0 0 0 0 1 1 1 0 0 0

0 0 0 0 1 0 0 1 0 0

0 0 0 0 0 1 0 0 1 0

0 0 1 0 0 0 1 0 1 0

0 1 0 1 0 1 0 0 1 0

0 1 0 0 1 1 0 1 1 0

0 0 1 0 0 0 0 1 0 0

0 0 0 1 1 1 1 1 0 0

0 0 0 0 0 0 0 0 0 0

【样例输出】area.out

    15

5、关系网络

【问题描述】

有 n 个人,他们的编号为 1~n,其中有一些人相互认识,现在 x 想要认识 y,可以通过他所认识的人来认识更多的人(如果 x 认识 y、y 认识 z,那么 x 可以通过 y 来认识 z),求出 x 最少需要通过多少人才能认识 y。

【输入格式】

第 1 行 3 个整数 n、x、y,n≤100,1≤x、y≤n。

接下来是一个 n×n 的邻接矩阵,a[i,j]=1 表示 i 认识 j,0 表示不认识。  

保证 i=j 时,a[i,j]=0,并且 a[i,j]=a[j,i]。行中的每两个数之间用一个空格分开。

【输出格式】

输出一行一个数,表示 x 认识 y 最少需要通过的人数。

【样例输入】

5 1 5

0 1 0 0 0

1 0 1 1 0

0 1 0 1 0

0 1 1 0 1

0 0 0 1 0

【样例输出】

2

【问题分析】

本题是典型的“求最优值”问题,可以通过经典的“宽度优先搜索”算法解决,使用队列维护。

6、最少经过城市

下图表示的是从城市A到城市H的交通图。从图中可以看出,从城市A到城市H要经过若干个城市。现要找出一条经过城市最少的一条路线。并输出分别经过哪几个城市。

​​C++ 算法篇 广度(宽度)优先搜索(BFS)_第3张图片

输入

n 表示有n个城市,接下来是n行,表示n×n 的邻接矩阵

​8
1 0 0 0 1 0 1 1
0 1 1 1 1 0 1 1
0 1 1 0 0 1 1 1
0 1 0 1 1 1 0 1
1 1 0 1 1 1 0 0
0 0 1 1 1 1 1 0
1 1 1 0 0 1 1 0
1 1 1 1 0 0 0 1

输出:

H--F--A

【算法分析】

看到这图很容易想到用邻接距阵来表示,0表示能走,1表示不能走。如图。

C++ 算法篇 广度(宽度)优先搜索(BFS)_第4张图片

城市H的交通图。从图中可以看出,从城市A到城市H要经过若干个城市。现要找出一条经过城市最少的一条路线。

首先想到的是用队列的思想。a数组是存储扩展结点的队列,a[i]记录经过的城市,b[i]记录前趋城市,这样就可以倒推出最短线路。具体过程如下:

(1) 将城市A入队,队首为0、队尾为1。

(2)将队首所指的城市所有可直通的城市入队(如果这个城市在队列中出现过就不入队,可用一布尔数组s[i]来判断),将入队城市的前趋城市保存在b[i]中。然后将队首加1,得到新的队首城市。重复以上步骤,直到搜到城市H时,搜索结束。利用b[i]可倒推出最少城市线路。

7、营救

【问题描述】

        铁塔尼号遇险了!他发出了求救信号。距离最近的哥伦比亚号收到了讯息,时间就是生命,必须尽快赶到那里。

        通过侦测,哥伦比亚号获取了一张海洋图。这张图将海洋部分分化成n*n个比较小的单位,其中用1标明的是陆地,用0标明是海洋。船只能从一个格子,移到相邻的四个格子。

       为了尽快赶到出事地点,哥伦比亚号最少需要走多远的距离。

【输入格式】

第一行为n,下面是一个n*n的0、1矩阵,表示海洋地图

最后一行为四个小于n的整数,分别表示哥伦比亚号和铁塔尼号的位置。

【输出格式】

哥伦比亚号到铁塔尼号的最短距离,答案精确到整数。

【输入样例】save.in

3

001

101

100

1 1 3 3

【输出样例】

4

【数据范围】

N<=1000

8、最少转弯问题(TURN)

【问题描述】

        给出一张地图,这张地图被分为n×m(n,m<=100)个方块,任何一个方块不是平地就是高山。平地可以通过,高山则不能。现在你处在地图的(x1,y1)这块平地,问:你至少需要拐几个弯才能到达目的地(x2,y2)?你只能沿着水平和垂直方向的平地上行进,拐弯次数就等于行进方向的改变(从水平到垂直或从垂直到水平)的次数。例如:如图,最少的拐弯次数为5。

C++ 算法篇 广度(宽度)优先搜索(BFS)_第5张图片

【输入格式】

第1行:n   m

第2至n+1行:整个地图地形描述(0:空地;1:高山),

如(图)第2行地形描述为:1 0 0 0 0 1 0

               第3行地形描述为:0 0 1 0 1 0 0

               ……

第n+2行:x1  y1  x2  y2  (分别为起点、终点坐标)

【输出格式】

s (即最少的拐弯次数)

【输入输出样例】(见图):

TURN.IN

TURN.OUT

5 7

1 0 0 0 0 1 0

0 0 1 0 1 0 0

0 0 0 0 1 0 1

0 1 1 0 0 0 0

0 0 0 0 1 1 0

1 3 1 7

5

9、拯救oibh总部

题目背景

oibh总部突然被水淹没了!现在需要你的救援……

题目描述

oibh被突来的洪水淹没了>.<还好oibh总部有在某些重要的地方起一些围墙,用*号表示,而一个封闭的*号区域洪水是进不去的……现在给出oibh的围墙建设图,问oibh总部没被淹到的重要区域(由"0"表示)有多少。

输入格式

第一行是两个数,x和y(x,y<=500)

第二行及以下是一个由*和0组成的x*y的图。

输出格式

输出没被水淹没的oibh总部的“0”的数量。

输入输出样例

输入

样例输入1
4 5
00000
00*00
0*0*0
00*00

样例输入2
5 5
*****
*0*0*
**0**
*0*0*
*****

输出 

样例输出1
1

样例输出2
5

10、血色先锋队

题目描述

巫妖王的天灾军团终于卷土重来,血色十字军组织了一支先锋军前往诺森德大陆对抗天灾军团,以及一切沾有亡灵气息的生物。孤立于联盟和部落的血色先锋军很快就遭到了天灾军团的重重包围,现在他们将主力只好聚集了起来,以抵抗天灾军团的围剿。可怕的是,他们之中有人感染上了亡灵瘟疫,如果不设法阻止瘟疫的扩散,很快就会遭到灭顶之灾。大领主阿比迪斯已经开始调查瘟疫的源头。原来是血色先锋军的内部出现了叛徒,这个叛徒已经投靠了天灾军团,想要将整个血色先锋军全部转化为天灾军团!无需惊讶,你就是那个叛徒。在你的行踪败露之前,要尽快完成巫妖王交给你的任务。

军团是一个 nn 行 mm 列的矩阵,每个单元是一个血色先锋军的成员。感染瘟疫的人,每过一个小时,就会向四周扩散瘟疫,直到所有人全部感染上瘟疫。你已经掌握了感染源的位置,任务是算出血色先锋军的领主们感染瘟疫的时间,并且将它报告给巫妖王,以便对血色先锋军进行一轮有针对性的围剿。

输入格式

第 11 行:四个整数 nn,mm,aa,bb,表示军团矩阵有 nn 行 mm 列。有 aa 个感染源,bb 为血色敢死队中领主的数量。

接下来 aa 行:每行有两个整数 xx,yy,表示感染源在第 xx 行第 yy 列。

接下来 bb 行:每行有两个整数 xx,yy,表示领主的位置在第 xx 行第 yy 列。

输出格式

第 11 至 bb 行:每行一个整数,表示这个领主感染瘟疫的时间,输出顺序与输入顺序一致。如果某个人的位置在感染源,那么他感染瘟疫的时间为 00。

输入输出样例

输入 

5 4 2 3
1 1
5 4
3 3
5 3
2 4

输出 

3
1
3

说明/提示

输入输出样例 1 解释

如下图,标记出了所有人感染瘟疫的时间以及感染源和领主的位置。

C++ 算法篇 广度(宽度)优先搜索(BFS)_第6张图片

数据规模与约定

对于 100% 的数据,保证 1≤n,m≤500,1≤a,b≤10^5。

11、棋盘

题目描述

有一个m×m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。

任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的), 你只能向上、 下、左、 右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费 1个金币。

另外, 你可以花费 2 个金币施展魔法让下一个无色格子暂时变为你指定的颜色。但这个魔法不能连续使用, 而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法; 只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。

现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少?

输入格式

第一行包含两个正整数m,n,以一个空格分开,分别代表棋盘的大小,棋盘上有颜色的格子的数量。

接下来的n行,每行三个正整数x,y,c, 分别表示坐标为(x,y)的格子有颜色c。

其中c=1 代表黄色,c=0 代表红色。 相邻两个数之间用一个空格隔开。 棋盘左上角的坐标为(1,1),右下角的坐标为(m,m)。

棋盘上其余的格子都是无色。保证棋盘的左上角,也就是((1,1) 一定是有颜色的。

输出格式

一个整数,表示花费的金币的最小值,如果无法到达,输出−1。

输入输出样例

输入 

5 7
1 1 0
1 2 0
2 2 1
3 3 1
3 4 0
4 4 1
5 5 0

输出 

8

输入 

5 5
1 1 0
1 2 0
2 2 1
3 3 1
5 5 0

输出 #2复制

-1

说明/提示

输入输出样例 1 说明

C++ 算法篇 广度(宽度)优先搜索(BFS)_第7张图片

从(1,1)开始,走到(1,2)不花费金币

从(1,2)向下走到(2,2)花费 1 枚金币

从(2,2)施展魔法,将(2,3)变为黄色,花费 2 枚金币

从(2,2)(2,2)走到(2,3)(2,3)不花费金币

从(2,3)走到(3,3)不花费金币

从(3,3)走到(3,4)花费 1 枚金币

从(3,4)走到(4,4)花费 1 枚金币

从(4,4)施展魔法,将(4,5)变为黄色,花费2 枚金币,

从(4,4)走到(4,5)不花费金币

从(4,5)走到(5,5)花费 1 枚金币

共花费 88枚金币。

输入输出样例 2 说明

C++ 算法篇 广度(宽度)优先搜索(BFS)_第8张图片

从(1,1)走到(1,2),不花费金币

从(1,2)走到(2,2),花费1金币

施展魔法将(2,3)变为黄色,并从(2,2)走到(2,3)花费2 金币

从(2,3)走到(3,3)不花费金币

从(3,3)只能施展魔法到达(3,2),(2,3),(3,4),(4,3)

而从以上四点均无法到达(5,5),故无法到达终点,输出-1

数据规模与约定

对于 30%的数据, 1≤m≤5,1≤n≤10。

对于 60%的数据,1≤m≤20,1≤n≤200。

对于 100%的数据, 1≤m≤100,1≤n≤1,000。

你可能感兴趣的:(算法篇,宽度优先搜索(BFS),算法,c++,宽度优先)