广度优先遍历(Breadth_First_Search),又称为广度优先搜索,简称BFS。
图的BFS类似于树的层序遍历。
广度优先遍历
搜索问题一般有两种情况:一种是给出初始结点,要求寻找符合约束条件的目标结点;另一种是给出初始结点和目标结点,要求找到从初始结点到目标结点的一条路径。对于解的要求也不尽相同,有的问题要求出一个可行解,有的则要求出最优解,还有的问题要找出全部解,有时还要按照一定的顺序输出。
搜索问题中的数据结构一般要求表达要合理,有助于计算机的处理;信息要完整,能反应出状态的本质和状态之间的关系;还要节省存储空间,尽可能地提高搜索的速度。比如分油问题本来用一个三元组(x10,x7,x3)即可,但是为了输出方便,我们增加一个d,用来表示该结点的父结点(由谁拓展而来),即变成一个四元组(x10,x7,x3,d)。另外,还会用到队列来实现控制策略。当然,不能忘记状态变化过程中的重复性检查,比如哈希表,因为大多数情况下,出现重复状态是毫无意义的,会造成死循环和空间的浪费。
宽度优先搜索(Breadth First Search,BFS),简称宽搜,又称广度优先搜索。它是从初始结点开始,应用产生式规则和控制策略生成第一层结点,同时检查目标结点是否在这些生成的结点中。若没有,再用产生式规则将所有第一层结点逐一拓展,得到第二层结点,并逐一检查第二层结点是否包含目标结点。若没有,再用产生式规则拓展第二层结点。如此依次拓展,检查下去,直至发现目标结点为止。如果拓展完所有结点,都没有发现目标结点,则问题无解。
对于以上“无向图”,从顶点 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;
}
}
}
【问题描述】
在一个 w×h 的矩形广场上,每一块 1×1 的地面都铺设了红色或黑色的瓷砖。小林同学站在某一块黑色的瓷砖上,他可以从此处出发,移动到上、下、左、右四个相邻的且是黑色的瓷砖上。现在,他想知道,通过重复上述移动所能经过的黑色瓷砖数。
【输入格式】
第 1 行为 h、w,2≤w、h≤50,之间由一个空格隔开。
以下为一个 w 行 h 列的二维字符矩阵,每个字符为“.”“#”“@”,分别表示该位置为黑色的瓷砖、红色的瓷砖,以及小林的初始位置。
【输出格式】
输出一行一个整数,表示小林从初始位置出发可以到达的瓷砖数。
【输入输出样例】
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
【问题分析】
本题是典型的“求连通块”问题,可以采用经典的“宽度优先搜索”算法求解,使用队列维护。
题目描述
由数字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
题目描述
一矩形阵列由数字 0 到 9 组成,数字 1 到 9 代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。
输入格式
第一行两个整数代表矩阵大小 n 和 m。
接下来 n 行,每行一个长度为 m 的只含字符 0
到 9
的字符串,代表这个n×m 的矩阵。
输出格式
一行一个整数代表细胞个数。
输入输出样例
输入
4 10
0234500067
1034560500
2045600671
0000000089
输出
4
说明/提示
数据规模与约定
对于 100% 的数据,保证 1≤n,m≤100。
编程计算由“*”号围成的下列图形的面积。面积计算方法是统计*号所围成的闭合曲线中水平线和垂直线交点的数目。如下图所示,在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 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
【问题描述】
有 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
【问题分析】
本题是典型的“求最优值”问题,可以通过经典的“宽度优先搜索”算法解决,使用队列维护。
下图表示的是从城市A到城市H的交通图。从图中可以看出,从城市A到城市H要经过若干个城市。现要找出一条经过城市最少的一条路线。并输出分别经过哪几个城市。
输入
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表示不能走。如图。
城市H的交通图。从图中可以看出,从城市A到城市H要经过若干个城市。现要找出一条经过城市最少的一条路线。
首先想到的是用队列的思想。a数组是存储扩展结点的队列,a[i]记录经过的城市,b[i]记录前趋城市,这样就可以倒推出最短线路。具体过程如下:
(1) 将城市A入队,队首为0、队尾为1。
(2)将队首所指的城市所有可直通的城市入队(如果这个城市在队列中出现过就不入队,可用一布尔数组s[i]来判断),将入队城市的前趋城市保存在b[i]中。然后将队首加1,得到新的队首城市。重复以上步骤,直到搜到城市H时,搜索结束。利用b[i]可倒推出最少城市线路。
【问题描述】
铁塔尼号遇险了!他发出了求救信号。距离最近的哥伦比亚号收到了讯息,时间就是生命,必须尽快赶到那里。
通过侦测,哥伦比亚号获取了一张海洋图。这张图将海洋部分分化成n*n个比较小的单位,其中用1标明的是陆地,用0标明是海洋。船只能从一个格子,移到相邻的四个格子。
为了尽快赶到出事地点,哥伦比亚号最少需要走多远的距离。
【输入格式】
第一行为n,下面是一个n*n的0、1矩阵,表示海洋地图
最后一行为四个小于n的整数,分别表示哥伦比亚号和铁塔尼号的位置。
【输出格式】
哥伦比亚号到铁塔尼号的最短距离,答案精确到整数。
【输入样例】save.in
3
001
101
100
1 1 3 3
【输出样例】
4
【数据范围】
N<=1000
【问题描述】
给出一张地图,这张地图被分为n×m(n,m<=100)个方块,任何一个方块不是平地就是高山。平地可以通过,高山则不能。现在你处在地图的(x1,y1)这块平地,问:你至少需要拐几个弯才能到达目的地(x2,y2)?你只能沿着水平和垂直方向的平地上行进,拐弯次数就等于行进方向的改变(从水平到垂直或从垂直到水平)的次数。例如:如图,最少的拐弯次数为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 |
题目背景
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
题目描述
巫妖王的天灾军团终于卷土重来,血色十字军组织了一支先锋军前往诺森德大陆对抗天灾军团,以及一切沾有亡灵气息的生物。孤立于联盟和部落的血色先锋军很快就遭到了天灾军团的重重包围,现在他们将主力只好聚集了起来,以抵抗天灾军团的围剿。可怕的是,他们之中有人感染上了亡灵瘟疫,如果不设法阻止瘟疫的扩散,很快就会遭到灭顶之灾。大领主阿比迪斯已经开始调查瘟疫的源头。原来是血色先锋军的内部出现了叛徒,这个叛徒已经投靠了天灾军团,想要将整个血色先锋军全部转化为天灾军团!无需惊讶,你就是那个叛徒。在你的行踪败露之前,要尽快完成巫妖王交给你的任务。
军团是一个 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 解释
如下图,标记出了所有人感染瘟疫的时间以及感染源和领主的位置。
数据规模与约定
对于 100% 的数据,保证 1≤n,m≤500,1≤a,b≤10^5。
题目描述
有一个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 说明
从(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枚金币。
从(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。