什么是多源最短路问题?
多源最短路问题(Multi-Source Shortest Path Problem,MSSP)是图论中的一个经典问题,它的目标是在给定图中找到从多个源点到所有其他顶点的最短路径。这个问题可以视为单源最短路问题(Single-Source Shortest Path Problem, SSSP)的扩展。
什么是单源最短路问题呢?其实我们上次讲的就可以归结在单元最短路问题当中,其实单源最短路问题就是只有一个起点对应一个终点,求最短路径,而多源最短路问题则是多个起点,对应一个终点,求这多个起点到达终点的最短路径,那这种题我们该怎么做呢?
第一种做法就是将多源最短路问题转换为n个单源最短路问题,循环n次就解决了,但是这种做法是非常慢的。
第二种做法就是把多个节点看成一个整体进行一次单源最短路问题的解法。
这是单源最短路问题问题:
多源最短路问题:
我们可以将多源最短路问题的节点看成一个整体,这种方法不仅在计算机领域很常用,在物理数学也很常用,这种方法叫隔离法,我们可以忽略每个节点之间的差异省去了我们比较每个节点差异的过程。
1.矩阵
题目链接
题目:
样例输出和输入:
这道题大致的意思就是对一个矩阵做变化,这个矩阵中的数只有两种,一种是1一种是0,我们该如何变换呢?根据题意,变换后节点的值为当前节点的值离最近一个0的节点的距离。按照这个规律首先我们来看看下面的例子,首先零肯定是不会变的,因为零距离最近的零就是他本身,所以这里距离就是0,第二行的1距离最近的零很显然是上左右的零,距离都是1,第三行的1距离醉经的0是上面的0距离为1,但是第三行中间的零距离最近的零是2.
算法原理:
这里我们已经讲过了做这种题的模式,我们只需要先将所有的零全入到队列中,这些零看成一个整体,在入队列的过程中顺便可以把需要返回的distance数组初始化为-1,然后零的对应位置赋值为0,这里我们直接利用单元最短路向外广搜,也就是整体向外扩散。
这里红色部分表示我们第一次入进去的0,蓝色部分表示我们第一次扩散,第一次扩散出来的部分应该填1,然后接下来可以继续向外扩散,这里就不展示了。
代码展示:
class Solution {
public:
typedef pair<int, int> PII;
int dx[4] = { 0,0,1,-1 };
int dy[4] = { 1,-1,0,0 };
vector<vector<int>> updateMatrix(vector<vector<int>>& mat)
{
int m = mat.size();
int n = mat[0].size();
vector<vector<int>> distance(m, vector<int>(n, -1));
queue<PII> q;
for (int i = 0;i < m;i++)
{
for (int j = 0;j < n;j++)
{
if (mat[i][j] == 0)
{
q.push({ i,j });
distance[i][j] = 0;
}
}
}
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 < m && y >= 0 && y < n&& distance[x][y] == -1)
{
distance[x][y] = distance[a][b] + 1;
q.push({ x,y });
}
}
}
return distance;
}
};
2.飞地的数量
题目链接
题目:
样例输出和输入:
这道题的大致意思就是让我们找于边界不联通的1的部分,可以看见第一个例子中的1于外界联通,所以这个1不是最终结果,示例1的三个1周围都是1,1代表海洋,所以这三个1和外界不联通所以这三个1是合法的1,返回这三个1,就是返回3.
算法原理:
上面这个例子红色代表边界上的与外界相连的1,蓝色代表周围都是0的1,这个例子很显然是返回1的,但是如果我们正面做的话很难,因为我们不知道它是否是与外界相连的,只有把这个岛屿遍历完了才知道是与外界相连的,所以这道题我们正难则反找中间的岛屿不好找,我们直接先把与外界相连的岛屿给标记了,然后对这个二维数组遍历一遍,返回没有被标记过的1的个数。这里具体一点就是对这个二维数组最外面的一层用一次多源BFS,先把所有在外面的1入进队列中,然后并标记,表示这个1已经被访问过了,并且不是内部的岛屿,然后再遍历一遍数组,找到没有被标记的1的个数。
代码展示:
class Solution
{
public:
typedef pair<int,int> PII;
bool vis[501][501];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int m,n;
int numEnclaves(vector<vector<int>>& grid)
{
queue<PII> q;
m=grid.size();
n=grid[0].size();
for(int i=0;i<m;i++)
{
if(grid[i][0]==1)
{
q.push({i,0});
vis[i][0]=true;
}
if(grid[i][n-1]==1)
{
q.push({i,n-1});
vis[i][n-1]=true;
}
}
for(int i=0;i<n;i++)
{
if(grid[0][i]==1)
{
q.push({0,i});
vis[0][i]=true;
}
if(grid[m-1][i]==1)
{
q.push({m-1,i});
vis[m-1][i]=true;
}
}
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<m&&y>=0&&y<n&&grid[x][y]==1&&vis[x][y]==false)
{
q.push({x,y});
vis[x][y]=true;
}
}
}
int step=0;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(grid[i][j]==1&&vis[i][j]==false)
{
step++;
}
}
}
return step;
}
};
3.地图的最高点
题目链接
题目:
这道题其实是和第一道题是一样的
样例输出和输入:
这道题和第一题比较相似。
我们来看示例2,首先很明显,这道题需要我们返回一个二维矩阵,然后每个元素的特点就是距离最近的一个1的距离,很显然给出的示例中,1里1最近当然就返回的是0,第一个0距离最近的也是1,所以我们只需要照搬第一个题的算法即可,可以说和第一个题一模一样。
代码展示:
class Solution
{
public:
typedef pair<int,int> PII;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int m,n;
vector<vector<int>> highestPeak(vector<vector<int>>& isWater)
{
m=isWater.size();
n=isWater[0].size();
vector<vector<int>> answer(m,vector<int>(n,-1));
queue<PII> q;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(isWater[i][j]==1)
{
answer[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<m&&y>=0&&y<n&&answer[x][y]==-1)
{
q.push({x,y});
answer[x][y]=answer[a][b]+1;
}
}
}
return answer;
}
};
4.地图分析
题目链接
题目:
样例输出和输入:
这道题其实也是和第一道题一样的但是这道题要求变化后的二维数组中最大的那个数,并返回。
代码展示:
class Solution {
public:
typedef pair<int,int> PII;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int m,n;
int maxDistance(vector<vector<int>>& grid) {
m=grid.size();
n=grid[0].size();
vector<vector<int>> dist(m,vector<int>(n,-1));
queue<PII> q;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(grid[i][j]==1)
{
q.push({i,j});
dist[i][j]=0;
}
}
}
int Max=-1;
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<m&&y>=0&&y<n&&grid[x][y]==0&&dist[x][y]==-1)
{
q.push({x,y});
dist[x][y]=dist[a][b]+1;
Max=dist[x][y];
}
}
}
return Max;
}
};
总结
通过本文对BFS算法在解决多源最短路问题中的应用介绍,可以看出BFS在处理无权图的最短路径问题时具有显著优势。它不仅操作简单、直观易懂,而且其广度优先的特点使得它在寻找最短路径时非常高效。多源最短路问题在实际生活中有着广泛的应用,例如交通网络中的最短路径计算、社交网络中的影响力传播等。
在实现过程中,我们需要注意以下几点:
初始化多源节点:确保所有源节点都被正确加入队列,并且其初始距离设置为0。
处理并行搜索:合理安排队列的扩展,保证所有节点都能被正确访问。
避免重复访问:使用访问标记或距离数组来避免节点被重复处理,提高算法效率。
通过实际案例,我们可以看到BFS在解决多源最短路问题时的高效性和可靠性。希望通过这篇文章,读者能够更好地理解BFS算法的应用场景及其实现方法,为今后的算法学习和实际应用提供帮助。
如有任何疑问或建议,欢迎在评论区留言讨论。