多源BFS

173. 矩阵距离 - AcWing题库

题目大意:让我们求所有点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;
}


1076. 迷宫问题 - AcWing题库

#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;
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值