多源BFS
题目大意:让我们求所有点0到所有1中的最短距离
总结一下:
先上结论:在使用BFS求最短路的时候,一个点第一次加入队列时候的距离,就是它的最短距离。
这里可以用反证法,设第二次加入队列的距离一定比第一次加入队列时的距离要小。如果它初次加入队列的时候不是最短距离,那么它在出队后还会加入队列,而它第二次入队的时候,肯定会多走k步(K>=0),这样它第二次加入队列的距离一定大于等于第一次加入队列的距离,这就与假设矛盾。
由于题目求的是所有点0到点1的最短距离,我们可以让一个虚拟源点指向所有点1,意思是这个虚拟源点可以走到所有点1,那么,我们可以把题目转化为求这个虚拟源点到所有点0的最短距离,这个最短距离就是虚拟源点第一次走到某个点0的距离。并且,这个虚拟源点的距离为0(自己到自己肯定为0),所以我们可以直接把这个虚拟源点放入队列,然后遍历就行了。
当然,这个虚拟源点只是方便我们理解,实际上加入队列的是所有点1,他们的集合就是一个虚拟源点。
另外,上述反向求最短路的思想,适用于很多场景,例如:让我们求一个点a到点b的最短距离,并输出这个最短路径,如果我们从a开始dfs到b,那么我们只能递归输出路径,或者借助数组,vector逆序输出,不是很方便。
如果我们反着求最短路,那么我们可以直接从a输出路径,比较方便。
例如:1076. 迷宫问题 - AcWing题库1076. 迷宫问题 - AcWing题库1076. 迷宫问题 - AcWing题库
#include <iostream>
#include <cstring>
#include <algorithm>
#define x first
#define y second
using namespace std;
const int N = 1010;
typedef pair<int, int> PII;
int n, m;
char g[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dist[N][N];
PII q[N * N];//用数组模拟队列虽然快,但在网格类型的图中一定要记得开N^2的空间哦!
void dfs()
{
//将距离数组设置成一个特殊的数(不可能取到的数)
//就可以实现既记录距离,又可以判重(一个点有没有重复走过)的功能
memset(dist, -1, sizeof dist);
//先把所有1放入队列
int hh = 0, tt = -1;
for(int i = 0; i < n; i ++ )
for(int j = 0; j < m; j ++ )
if(g[i][j] == '1')
{
q[++ tt] = {i, j};
dist[i][j] = 0;
}
while(hh <= tt)
{
PII t = q[hh ++ ];//这里不建议使用auto,暂时不知道原因!
int distance = dist[t.x][t.y];
for(int i = 0; i < 4; i ++)
{
int a = t.x + dx[i], b = t.y + dy[i];
if(a < 0 || a >= n || b < 0 || b >= m) continue;//越界
if(dist[a][b] != -1) continue;//去重
dist[a][b] = distance + 1;
q[ ++ tt] = {a, b};
}
}
}
int main()
{
cin >> n >> m;
for(int i = 0; i < n; i ++ ) cin >> g[i];//由于题目给出的字符是连续的,所以直接读入一个字符串
dfs();
for(int i = 0; i < n; i ++ )
{
for(int j = 0; j < m; j ++ )
cout << dist[i][j] << " ";
cout << endl;
}
return 0;
}
#include <iostream>
#include <cstring>
#include <algorithm>
#define debug2(v1, v2) \
cout << #v1 << " = " << v1 << " " << #v2 << " = " << v2 << endl
#define debug1(v) \
cout << #v << " = " << v << endl
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int n, g[N][N];
PII q[N * N], pre[N][N];
void bfs()
{
memset(pre, -1, sizeof pre);
int hh = 0, tt = -1;
q[++ tt] = {n - 1, n - 1};//逆向bfs
pre[n - 1][n - 1] = {n, n};
//如果不加这一句,下面输出的时候结束条件使用pre[n-1][n-1].x!=-1就会死循环
//因为我们的pre[n-1][n-1]=-1,所以在dfs的时候会被遍历到,值会被修改
while(hh <= tt)
{
PII t = q[hh ++ ];
for(int i = 0; i < 4; i ++ )
{
int a = t.x + dx[i], b = t.y + dy[i];
if(a < 0 || a >= n || b < 0 || b >= n) continue;
if(pre[a][b].x != -1) continue;
if(g[a][b] == 1) continue;
pre[a][b] = {t.x, t.y};
q[ ++ tt] = {a, b};
if(a == 0 && b == 0) return ;
}
}
}
int main()
{
cin >> n;
for(int i = 0; i < n; i ++ )
for(int j = 0; j < n; j ++ )
cin >> g[i][j];
bfs();
PII end(0, 0); //PII初始化
while(0)
{
// debug2(end.x, end.y);
cout << end.x << " " << end.y << endl;
if(end.x == n - 1 && end.y == n - 1) break;
end = pre[end.x][end.y];
}
return 0;
}