$\color{green}{<–画图不易,点下这里赞一下吧}$
广度优先遍历。
思路:从起点开始,往前走第一步,记录下所有第一步能走到的点,然后从所第一步能走到的点开始,往前走第二步,记录下所有第二步能走到的点,重复下去,直到走到终点。输出步数即可。
这就是广度优先遍历的思路。
实现方式:广度优先遍历
- 用 g 存储地图,f存储起点到其他各个点的距离。
- 从起点开始广度优先遍历地图。
- 当地图遍历完,就求出了起点到各个点的距离,输出f[n][m]即可。
void bfs(int a, int b)
: 广度优遍历函数。输入的是起点坐标。queue<PII> q;
:用来存储每一步走到的点。while(!q.empty())循环
:循环依次取出同一步数能走到的点,再往前走一步。int dx[4] = {0, 1, 0, -1}, dy[4] = {-1, 0, 1, 0};
:一个点往下一步走得时候,可以往上下左右四方向走。
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
int g[N][N];//存储地图
int f[N][N];//存储距离
int n, m;
void bfs(int a, int b)//广度优先遍历
{
queue<PII> q;
q.push({a, b});
//初始点的距离为 0.
//可以不要这一句,因为f初始化的时候,各个点为0
f[0][0] = 0;
while(!q.empty())
{
PII start = q.front();
q.pop();
//这一句可以不要,因为入队的时候就置为了1
g[start.first][start.second] = 1;
int dx[4] = {0, 1, 0, -1}, dy[4] = {-1, 0, 1, 0};
for(int i = 0; i < 4; i++)//往四个方向走
{
//当前点能走到的点
int x = start.first + dx[i], y = start.second + dy[i];
//如果还没有走过
if(g[x][y] == 0)
{
//走到这个点,并计算距离
g[x][y] = 1;
f[x][y] = f[start.first][start.second] + 1;//从当前点走过去,则距离等于当前点的距离+1.
//这个点放入队列,用来走到和它相邻的点。
q.push({x, y});
}
}
}
cout << f[n][m];
}
int main()
{
memset(g, 1, sizeof(g));
cin >> n >>m;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
cin >> g[i][j];
}
}
bfs(1,1);
}
有问题直接评论即可,求点赞~
因为当第一个点到达终点时,它一定是最短距离,并且会将终点变成墙,那么其他点再也无法到达终点,也更新不了初始点到终点的距离。
说的真好啊!
6
为什么第一个点到达终点后一定是最小距离
这就是bfs的特点,队列中存储的队首元素A出队,假设A的相邻点是终点,那么终点到起点路程=A->起点的路程+1,然后终点就被标记成了走过了,现在队首A出队,B成为新队首,可以根据bfs的特点,后入队的元素的距离都是先入队的元素的距离+1,即B->起点路程 >= A->起点路程,假设B也是终点的相邻点,那么B+1>=A+1肯定不行,因为终点此时已经被标记成了走过了,所以距离值是A+1,而不是B+1,就是最小了。
因为当第一个点到达终点时,终点就被变为了墙,后到达的点无法到达终点,那自然就不会更新距离
这里我问一下是不是不走回头路?
大家看y总的代码,把if语句加一个距离为-1(还没走过)才往下走
海绵宝宝我可爱死你啦!
👌🏻
大佬,请问那一步是保证找到的就是最短的呢,我脑子有点笨看不出来
bfs函数里面的if语句确保了最短距离,图里面只有没走过的点才会向下计算距离,当最短距离出来之后,出口的点就相当于走过了,所以不会有更长的出现了
个人理解,有误请谅解
挺清楚的,谢谢大佬
bfs每个点只遍历一次喔
BFS基本思想决定了第一次到达就是最终的点。因为她是一圈圈遍历的,可以仔细看一下y总视频
意思是每种可能都会走,但是将每一次情况走过时,最短的路径会先到达,而到达时就会结束循环,此时输出的就是最短的路径。但是有点不理解的就是怎样同时将每种情况同时走一遍
我也是这样理解的,但是神奇的是去掉标记走过代码
g[start.first][start.second] = 1;
也能ac,这是为什么?求解QAQ因为这句话仅仅是用来将初始点墙掉,而就算不墙,在下一步也会有一个情况重回初始点把初始点墙掉。但不影响结果。
入队的时候,置为了1,出队的时候再置为1,重复了,这句可以去掉
太棒了
最短路径到达后,应该没有立即退出循环吧?应该是标记终点,让其他路径无法到达。
大佬
其实太简单了,bfs是一层一层遍历的。每一层都是上一层状态的一步拓展。
海绵宝宝永远滴神
memset按字节赋值哦,int是四个字节、不能用memset全部初始化为1
写1,存储的不是1,。只要不是 0 或 1.这个题就能用。
是了,赞👍
不是只有0和-1才能正确赋值吗
嗯,是这样的
0的二进制是32个0,-1的二进制是32个1,只是说0和-1符合直观的按比特赋值定义,并不是说就不能赋其他值。对于这题,
memset(g, -1, sizeof(g));
的作用是将地图周围墙掉,防止越界,故第二个参数只要不是0就可以不是0就够了吧,不需要再保证不是1。就算地图之外是1,也没法再越界访问了。
还可以赋值无穷
我觉得BFS只需要在找到结果就输出答案,然后结束就可以了
#### 爱惨海绵宝宝了
宝儿哥!牛逼
主函数那块为什么要从1开始读入g数组,我从0开始读入,然后bfs(0,0)没有输出
我也想问
因为它memset(g,1,sizeof g)这个等于说把g全部变成了墙,而它输入进g的数时i和j是从1开始的,等于说0这一行和列都变成了墙,所以不能从0,0开始
因为它memset(g,1,sizeof g)这个等于说把g全部变成了墙,而它输入进g的数时i和j是从1开始的,等于说0这一行和列都变成了墙,所以不能从0,0开始
入队的时候置为1了,不是说memset不是初始化为1吗?
为什么x和y不需要判断边界呢
memset把g【N][N]初始化了,所以边界都是1,然后memset后面又给m与n范围内赋值0,来确定路径。
她留了0作为边界 所以上下找不会越界
能更新下怎么输出路径吗求求了
####
#include [HTML_REMOVED]
#include [HTML_REMOVED]
#include [HTML_REMOVED]
#include [HTML_REMOVED]
using namespace std;
typedef pair[HTML_REMOVED] PII;
const int N = 110;
int g[N][N];
int f[N][N];
int n, m;
void bfs(int a, int b) {
queue[HTML_REMOVED] q;
q.push({a, b});
while (!q.empty()) {
PII start = q.front();
q.pop();
g[start.first][start.second] = 1;
int dx[4]{1, 0, -1, 0}, dy[4]{0, -1, 0, 1};
}
void bfs_print() {
int target = f[n][m];
queue[HTML_REMOVED] q;
q.push({n, m});
cout << n << ” ” << m <<endl;
while (!q.empty()) {
PII start = q.front();
q.pop();
}
int main()
{
cin.tie(0);
memset(g, 1, sizeof (g));
cin >> n >> m;
for (int i = 1; i <= n; i ) {
for (int j = 1; j <= m; j ) {
cin >> g[i][j];
}
}
}
这代码不知道为什么超时了(为了防止我自己的问题,直接复制的)
你试试直接提交,别点调试
请问一下g[1][1]是怎么变成1的
我认为g[1][1]不是在被用完之后立马就变为1的 而是遍历g[1][2]的时候才将g[1][1]赋值为1的。这样有些数据可能会出错
memset(g, -1, sizeof(g));实在不明白这一步意思是在干嘛呀
后面不是要输入吗
为什么一开始要把g数组的值赋成一啊?
为什么vector[HTML_REMOVED] q;就会报错
PII q[N * N];这样才可以啊,我不理解这个是为什么,原则上不都是可以的吗?
为什么起点一定要为(1,1)呢,换成(0,0)就超时了
因为它memset(g,1,sizeof g)这个等于说把g全部变成了墙,而它输入进g的数时i和j是从1开始的,等于说0这一行和列都变成了墙,所以不能从0,0开始
这种方法怎么在bfs函数里面添加打印路径