宽度优先搜索(BFS)解决多源最短路问题(1)

69 阅读3分钟

前言

本文将会向你介绍有关宽度优先搜索(BFS)解决多源最短路问题的相关题型:矩阵、飞地的数量

引入

上一篇文章提到单源最短路问题,单源最短路问题就是给出一个起点,求到终点的最短距离,而多源最短路问题,有多个起点,求到终点的最短路径 多源BFS指的是,用BFS宽度优先搜索解决边权为1的多源最短路问题 那么该如何用BFS解决多个起点求其到终点的最短路这类问题呢? 解法一 将多源最短路问题转化为若干个单源最短路问题,即对每一个起点来一次BFS,但是这样大概率是会超时的

解法二 把所有的起点(源点)当成一个“超级源点”,问题就变成了单一的单源最短路问题 如何转化呢?

1、将所有的起点加入到队列里面

2、一层一层的往外扩展

在这里插入图片描述

矩阵

leetcode.cn/problems/2b…

题目要求

在这里插入图片描述

题目解析

题目要求求出每一个格子到0的最近距离,也就是求1到0的最短路,多个起点求最短路,那么这道题就是一道考察多源BFS的问题

解法一: 用单源BFS的思想,即对每一个起点来一次BFS,一层层地向外扩展,这样地做法可能会超时

解法二: 多源BFS,即把所有1当作一个超级源点,然后来一次BFS,可是这样也会有一个坑,当找到0后,我们需要更新1位置处的最短距离,每个“1”在遍历时都必须跟踪到最近“0”的距离,找到0后,并不是在0这个位置处标记最短距离,而是返回到1的位置处,这会使实现变得复杂

正难则反: 求0到1的最短路问题,因为1到0和0到1的最短路径一定是一样的,只需要先遍历整个矩阵,将0标记出来,然后将所有0添加到队列中一层层向外扩展即可。

如图所示

模拟了一遍向外扩展的过程(绿色为第一次扩展,红色为第二次,蓝色为第三次),已经搜索标记过的区域不需要再标记 在这里插入图片描述

代码如下

class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& mat) 
    {
        int dx[4] = {0, 0, -1, 1};
        int dy[4] = {-1, 1, 0, 0};
        queue<pair<int, int>> q;
        vector<vector<int>> dist(mat.size(), vector<int>(mat[0].size(), -1));;
        for(int i = 0; i < mat.size(); i++)
        {
            for(int j = 0; j < mat[0].size(); j++)
            {
                if(mat[i][j] == 0)
                {
                    dist[i][j] = 0;
                    q.push({i, j});
                }
            }
        }
        while(q.size())
        {
            auto [a, b] = q.front();
            q.pop();
            for(int i = 0; i < 4; i++)
            {
                int x = a + dx[i];
                int y = b + dy[i];
                if(x >= 0 && x < mat.size() && y >= 0 && y < mat[0].size() && dist[x][y] == -1)
                {
                    dist[x][y] = dist[a][b] + 1;
                    q.push({x, y});
                }

            }
        }
        return dist;
    }
};

飞地的数量

leetcode.cn/problems/nu…

题目要求

在这里插入图片描述

题目解析

题目要求返回被 0 海洋包围的 1 岛屿的个数,如果在边界的岛屿就不算被海洋包围,可以简单地想到遍历矩阵,遇到1,就来一次bfs,找到整块岛屿,但是有个问题,就是区分不了该岛屿是不是在边界上的

证难则反: 可以先将边界上的岛屿标记出来,剩下的则全部是被海洋包围的岛屿,即遍历边界上的1,遇到1,就来次bfs,找到整个岛屿并标记,然后两层for循环遍历矩阵即可

代码如下

class Solution {
public:
    queue<pair<int, int>> q;
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    int ret = 0; //飞地的数量
    //多源bfs
    void bfs(vector<vector<int>>& grid)
    {
        while(q.size())
        {
            auto [a, b] = q.front();
            q.pop();
            for(int i = 0; i < 4; i++)
            {
                int x = a + dx[i], y = b + dy[i];
                if(x >= 0 && x < grid.size() && y >= 0 && y < grid[0].size() && grid[x][y] == 1)
                {
                    grid[x][y]++;
                    q.push({x, y});
                }
            }
        }
    }
    int numEnclaves(vector<vector<int>>& grid) 
    {   
        for(int i = 0; i < grid.size(); i++)
        {
            for(int j = 0; j < grid[0].size(); j++)
            {
                if(grid[i][j] == 1 && (i == 0 || i == grid.size() - 1 || j == 0 || j == grid[0].size() - 1))
                {
                    q.push({i, j});
                    grid[i][j]++;
                }
            }
        }
        bfs(grid);
        //统计结果
        for(int m = 0; m < grid.size(); m++)
        {
            for(int n = 0; n < grid[0].size(); n++)
            {
                if(grid[m][n] == 1)
                {
                    ret++;
                }
            }
        }
        return ret;
    }
};