广度优先搜索
BFS,其英文全称是 Breadth First Search,意为广度优先搜索,是所有的搜索手段之一。它是从某个状态开始,将所有节点加入一个先进先出的队列,然后一层一层进行状态转移,并且展开节点。
广度优先搜索基本概念
作为搜索算法的一种,BFS 相较于 DFS 而言,BFS 是一层一层展开的,那么对于有多个终态时,最先找到的一定是最短的。
广度优先搜索算法的设计步骤
按照定义设计:
- 确定该题目的状态(包括边界)
- 找到状态转移方式
- 找到问题的出口,计数或者某个状态
- 设计搜索
会发现我们前期要找到的参数基本一致,所以在一般情况下 BFS 和 DFS 可以相互转换。
伪代码:
int check(参数)
{
if(满足条件)
return 1;
return 0;
}
bool pd(参数){
相应操作
}
void bfs()
{
1. 把根节点放入队列尾端
2. 每次从队列中取出一个节点
3. Check 判断是不是答案,如果是结束算法 return;
4. 把当前取出的节点扩展,如果扩展后的节点经Pd()后符合要求,就放入队列,不符合就不放。
5. 转到步骤2,循环执行
}
如果所有节点被扩展完了,没有找到答案就无解。
BFS原理
BFS搜索的原理:逐层扩散,从起点出发,按层次从近到远,逐层先后搜索
编码:用队列实现
应用:BFS一般用于求最短路径问题,BFS的特点是逐层搜索,先搜到的层离起点更近
BFS:找最短路径
找从@到*最短路径
步骤 | 出队列 | 进队列 | 当前队列内的点 |
---|---|---|---|
(1) | 1 | 1 | |
(2) | 1 | 2、3 | 2、3 |
(3) | 2 | 4、5、6 | 3、4、5、6 |
(4) | 3 | 7、8 | 4、5、6、7、8 |
最短路径问题用BFS
BFS的特点:逐层扩散
- 往BFS的队列中加入邻居结点时,按距离起点远近的顺序加入,先加入距离起点为1的邻居结点,加完之后,再加入距离为2的邻居结点,等等
- 搜完一层,才会继续搜下一层
最短路径:从起点开始,沿着每一层逐步往外走,每多一层,路径长度就增加1。
所有长度相同的最短路径都是从相同的层次扩散出去的
搜到第一个到达终点的路径,就是最短路径
长草
题目链接
难度: 简单
标签: 模拟, BFS, 2020, 省模拟赛
题目描述:
小明有一块空地,他将这块空地划分为 n 行 m 列的小块,每行和每列的长度都为 1。
小明选了其中的一些小块空地,种上了草,其他小块仍然保持是空地。
这些草长得很快,每个月,草都会向外长出一些,如果一个小块种了草,则它将向自己的上、下、左、右四小块空地扩展,
这四小块空地都将变为有草的小块。请告诉小明,k 个月后空地上哪些地方有草。
输入描述:
输入的第一行包含两个整数 n,m。
接下来 n 行,每行包含 m 个字母,表示初始的空地状态,字母之间没有空格。如果为小数点,表示为空地,如果字母为 g,表示种了草。
接下来包含一个整数 k。 其中,2≤n,m≤1000,1≤k≤1000
输出描述:
输出 n 行,每行包含 m 个字母,表示 k 个月后空地的状态。如果为小数点,表示为空地,如果字母为 g,表示长了草。
输入输出样例:
示例:
输入:
4 5
.g...
.....
..g..
.....
2
输出:
gggg.
gggg.
ggggg
.ggg.
运行限制:
最大运行时间:1s
最大运行内存: 256M
解题思路:
这个题目简直就是为了广度优先搜索设置模板题,由于这个题目是输出广度优先搜索 K
次扩展后的终态,那我们就不用设置判断函数。
这里用一个 N×M 的矩阵来表示草地。
- 算法开始:
将字母为g
的草地的位置加入队列,然后向下执行 - 判断边界:
判断是否已经长了草,判断是否超出边界范围 - 搜索过程:
不断从队列取出一个节点,进行上下左右的扩展,执行2
判断边界,符合就放入队列,不符合就跳过。
执行 K 次扩展,输出草地状态。 - check(参数):
这里不需要进行Check
,判断符不符合题意,有没有剪枝…
C++ 语言描述:
#include <bits/stdc++.h>
using namespace std;
const int M = 1005;
struct PII
{
int first;
int second;
};
// C++ 有个数据类型叫 pair 上面的就可以定义为 pair<int,int> 用起来比较方便。
PII tempPair;//临时结点
char Map[M][M];
//---------图的路径搜索常用方向移动表示-------
int dx[4]= {0,1,-1,0};
int dy[4]= {1,0,0,-1};
// 两两组合形成上下左右四个方向
// 1------------------> x
// |
// |
// |
// |
// |
// |
// |
// ↓
// y
// dx[0]=0 dy[0]=1 那么代表向下的方向
// dx[1]=1 dy[1]=0 那么代表向右的方向
// dx[2]=-1 dy[2]=0 那么代表向左的方向
// dx[3]=0 dy[3]=-1 那么代表向上的方向
int n;// n 行
int m;// m 列
int k;// k 次
queue<PII > q; //广度优先搜索所用的队列
int len;//记录节点数量方便后续k的计算
bool pd(int x, int y)
{
if(x<1)
return 0;
// /x 轴坐标 左侧越界
else if(x>n)
return 0;
//x 轴坐标 右侧越界
else if(y<1)
return 0;
//y 轴坐标 上侧越界
else if(y>m)
return 0;
//y 轴坐标 下侧越界
else if(Map[x][y]=='g')
return 0;
//已经长草了
else return 1;
// 在范围内,且没长草
}
void BFS()
{
//BFS
while(!q.empty()&&k>0)
{
tempPair = q.front();
q.pop();
//这两步是取出队首的节点
int x = tempPair.first;//横坐标
int y = tempPair.second;//纵坐标
for(int i=0; i<4; i++)
{
int nowx = x+dx[i]; //扩展后的横坐标
int nowy = y+dy[i]; //扩展后的纵坐标
if(pd(nowx,nowy))
{
q.push({nowx,nowy});
Map[nowx][nowy]='g';
}
//符合要求执行扩展,不符合要求,忽略即可。
}
len--; //每取出一个节点len -1
if(len==0)
{
//当len =0 时,代表当前层扩展完了,那么就代表第一个月扩展完了
k--; // 所以k--
len = q.size(); // 当前层扩展完了,那就该扩展下一层了,所以len又被赋值为下一层的节点数目的值
}
}
}
int main()
{
cin>>n>>m;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
cin>>Map[i][j];
if(Map[i][j]=='g')
{
tempPair.first=i;
tempPair.second=j;
// cout<<i<<""<<j<<endl;
q.push(tempPair);//将初始有树的结点加入队列
}
}
}
len = q.size();//记录第一层的节点数量方便后续k的计算
cin>>k;
BFS();
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
cout<<Map[i][j];
}
cout<<endl;
}
return 0;
}