多源 BFS_多源最短路问题

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃
1.多源 BFS_多源最短路问题
最短路径问题有单源最短路径、多源最短路径、边权为 1 的最短路径、带负环的最短路等等。目前我们学了,边权为 1 的最短路径。
单源最短路径
前面一个专题用BFS 解决 边权为 1 的最短路径问题,都是单源最短路径问题。
一个起点一个终点 求最短路径

多源最短路径
多个起点一个终点 求从多个起点出发到终点的最短路径问题。

多源 BFS
用 BFS 解决边权为 1 的多源最短路径问题。
不管是单源还是多源只要用 BFS 解决边权都是为1的。边权是其他的是不能用BFS解决。也就是只要是用BFS解决最短路径问题,必须都是边权为1 !
如何解决?
解法一:暴力,把多源最短路径问题转化成若干个单源最短路径问题。
也就是说给我若干个起点,用BFS暴力枚举出所有起点到终点的最短路径,然后取其中最短的路径。大概率是会超时的。
解法二:把所有的源点当成一个 超级源点,问题就变成了单一的单源最短路径问题
意思就是给我很多起点,我想办法把这些起点当成一个起点,问题就变长了从这一个超级起点开始到终点的最短路径了。仅需从这个超级起点出发来一次BFS就可以了。

这里可能会有疑惑,把所有起点当成一个超级起点出发最终得到的路径是正确答案吗?
这里我们感性的理解一下,所有起点的小人同时向外走一步。它们同时向外走一步,我们是可以舍去很多点的。比如有的起点小人向外走一步但是这一步已经被其他小人走过了,所以说从这个点向外走一步肯定没有其他点向外走一步更好,因此就可以舍去这个点。然后就相当于从超级起点出发向外走一步。把不好的都舍去只把好的保留,这就是超级源点干的事情。因为只保留好的,所以到达某一点绝对是最短的。

如何写代码呢?
这里的重点就是如何把所有起点搞成一个超级起点呢?特别简单,我们回忆一下 边权为1 单源最短路径问题 用 BFS 怎么解决,分为两步:
- 把起点加入到队列
- 一层一层往外扩
如何把所有起点搞成一个超级起点呢?
- 把所有起点加入到队列
- 一层一层往外扩
2.01 矩阵
题目链接: 542. 01 矩阵
‘
题目分析:

给一个m*n的矩阵mat,返回一个同等大小的矩阵并且矩阵中每个格子都是到达0的最短距离。

算法原理:
解法一:一个位置一个位置求
遍历一下矩阵,一个一个位置BFS求到0的最短距离,但是会超时。
解法二:多源BFS + 正难则反
我们可以把所有的1当成一个超级起点,然后从这个起点做一次BFS,但是如果把1作为起点有一个致命问题,你更新不出来结果,把1当成超级起点从1找到0之后那是那个1到达的0?并且又如何回到开始位置去更新1到0的最短距离呢?
正着来起点到终点最短距离,那反着来终点到起点最短距离也是没问题的。所以,把所有的 0 当成起点,1 当成终点,当扩展到1的时候直接把距离更新到对应1的位置就行了。
- 把所有的 0 位置加入到队列中
- 一层一层的向外扩即可
刚开始遍历一下把所有0加入到队列中同时也把dist中对应位置更新成0,然后一层一层往外扩,我们是把队列中元素拿出来上下左右四个方向像外扩。当层序遍历结束之后这个dist也就填完了。把dist返回就可以了。

这里有一些细节问题:
回忆求边权为 1 的单源最短路径,我们写代码时是需要一个bool类型vis二维数组标记当前位置是否被搜索过,并且层序遍历中还需要step记录当前扩展到那一层,sz记录当前队列中的元素是把队列中所有元素都往外扩一层,结合step知道当前扩展到这一层的步数是多少。
这道题我们仅需一个dist二维数组就行了,不需要上面三个东西。先看这个bool数组,其实我们可以把dist数组里面的值全部都初始化-1来标记当前位置没有被搜索过。step和sz也不用要,我们直接从dist数组中就可以更新下一个位置中dist的值,之前是从[a,b] 上下左右扩展 [x,y],这次从[a,b] 扩展到 [x,y] dist中已经记录[a,b]位置的值了,仅需dist[x][y] = dist[a][b] + 1 即可。而且也不用搞一个sz一层一层往外扩,我们可以一个元素一个元素往下扩,原因就是dist已经标记当前元素是处于那一层的。

class Solution {
int dx[4] = {
0, 0, 1, -1};
int dy[4] = {
1, -1, 0, 0};
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
int m = mat.size(), n = mat[0].size();
// dist[i][j] == -1 表⽰:没有搜索过
// dist[i][j] != -1 表⽰:最短距离
vector<vector<int>> dist(m,vector<int>(n,-1));
queue<pair<int,int>> q;
// 1. 把所有的源点加⼊到队列中
for(int i = 0; i < m; ++i)
for(int j = 0; j < n; ++j)
if(mat[i][j] == 0)
{
q.push({
i,j});
dist[i][j] = 0;
}
// 2. ⼀层⼀层的往外扩 (因为dist每个起点的开始层数了,因此不用再搞一个sz)


2536

被折叠的 条评论
为什么被折叠?



