蓝桥杯C++基础算法-最短路径Bellman-Ford算法
这段代码实现了一个Bellman-Ford算法,用于求解单源最短路径问题,特别适用于包含负权重边的图。Bellman-Ford算法可以检测图中是否存在负权重环,并且能够处理最多 k 次中转的情况。以下是代码的详细思路解析:
1. 问题背景
给定一个包含 n 个节点和 m 条边的图,每条边有一个权重。目标是找到从节点 1 到节点 n 的最短路径长度,最多允许经过 k 次中转。如果不存在这样的路径,或者路径长度超过某个阈值,则返回 "impossible"。
2. Bellman-Ford算法的概念
Bellman-Ford算法是一种经典的最短路径算法,适用于带权重的有向图,包括负权重边。算法通过多次松弛操作逐步更新从源点到所有其他节点的最短路径。它还可以检测图中是否存在负权重环。
3. 代码逻辑解析
(1) 初始化图和距离数组
cin >> n >> m >> k; // 输入节点数、边数和最大中转次数
for (int i = 0; i < m; i++)
{
int a, b, w;
cin >> a >> b >> w; // 输入一条边的起点、终点和权重
edges[i] = {a, b, w}; // 存储边的信息
}
-
输入图的节点数
n、边数m和最大中转次数k。 -
输入每条边的起点
a、终点b和权重w,并存储到edges数组中。
(2) Bellman-Ford算法实现
int bellman_ford()
{
memset(dist, 0x3f, sizeof dist); // 初始化距离数组为无穷大
dist[1] = 0; // 源点到自身的距离为0
for (int i = 0; i < k; i++) // 进行k次松弛操作
{
memcpy(backup, dist, sizeof dist); // 备份当前距离数组
for (int j = 0; j < m; j++) // 遍历所有边
{
int a = edges[j].a, b = edges[j].b, w = edges[j].w;
dist[b] = min(dist[b], backup[a] + w); // 更新节点b的距离
}
}
if (dist[n] > 0x3f3f3f3f / 2) return -1; // 如果目标节点的距离仍为无穷大,说明无路径
return dist[n]; // 返回源点到目标节点的最短路径长度
}
-
初始化:
-
将距离数组
dist初始化为无穷大。 -
将源点到自身的距离设为0。
-
-
松弛操作:
-
进行
k次松弛操作,每次松弛操作遍历所有边。 -
使用
backup数组备份当前距离数组,避免在一次松弛操作中多次更新同一个节点的距离。 -
对于每条边
(a, b),更新节点b的距离:dist[b] = min(dist[b], backup[a] + w)。
-
-
检查结果:
-
如果目标节点
n的距离仍为无穷大,说明从源点到目标节点无路径,返回 -1。 -
否则,返回源点到目标节点的最短路径长度。
-
(3) 主函数
int t = bellman_ford(); // 调用Bellman-Ford算法
if (t == -1) puts("impossible"); // 如果无路径,输出"impossible"
else cout << t; // 否则输出最短路径长度
-
调用
bellman_ford函数计算从节点 1 到节点n的最短路径长度。 -
根据返回值判断是否存在路径,并输出结果。
4. 示例运行
输入:
4 5 2
1 2 1
1 3 3
2 3 1
2 4 2
3 4 1
输出:
3
5. 总结
这段代码的核心思路是通过Bellman-Ford算法求解单源最短路径问题,特别适用于包含负权重边的图。算法通过多次松弛操作逐步更新从源点到所有其他节点的最短路径,并可以检测图中是否存在负权重环。这种方法的时间复杂度为 O(k × m),适用于中等规模的图。
完整代码
#include<bits/stdc++.h>
using namespace std;
// 定义最大节点数和最大边数
const int N = 510, M = 10010;
// n 表示节点数,m 表示边数,k 表示最多经过的边数
int n, m, k;
// dist 数组用于存储从起点到各个节点的最短距离
// backup 数组用于备份 dist 数组,防止串联更新
int dist[N], backup[N];
// 定义边的结构体,包含起点 a、终点 b 和边的权重 w
struct Edge
{
int a, b, w;
} edges[M];
// Bellman - Ford 算法实现
int bellman_ford()
{
// 初始化 dist 数组,将所有距离初始化为一个很大的值(0x3f3f3f3f)
memset(dist, 0x3f, sizeof dist);
// 起点到自身的距离为 0
dist[1] = 0;
// 进行 k 次迭代,每次迭代尝试更新所有边
for(int i = 0; i < k; i ++)
{
// 备份 dist 数组,防止串联更新
memcpy(backup, dist, sizeof dist);
// 遍历所有边
for(int j = 0; j < m; j ++)
{
// 取出当前边的起点、终点和权重
int a = edges[j].a, b = edges[j].b, w = edges[j].w;
// 尝试更新从起点到终点 b 的最短距离
// 使用 backup[a] 而不是 dist[a] 防止串联更新
dist[b] = min(dist[b], backup[a] + w);
}
}
// 由于存在负权边,可能会出现距离被更新为一个接近但不等于 0x3f3f3f3f 的值
// 所以判断是否无法到达终点时,使用大于 0x3f3f3f3f / 2 进行判断
if(dist[n] > 0x3f3f3f3f / 2) return -1;
// 返回起点到终点的最短距离
return dist[n];
}
int main()
{
// 输入节点数、边数和最多经过的边数
cin >> n >> m >> k;
// 循环读入每条边的信息
for(int i = 0; i < m; i ++)
{
// a 表示起点,b 表示终点,w 表示边的权重
int a, b, w;
cin >> a >> b >> w;
// 将边的信息存储到 edges 数组中
edges[i] = {a, b, w};
}
// 调用 Bellman - Ford 算法并存储结果
int t = bellman_ford();
// 如果无法到达终点,输出 "impossible"
if(t == -1) puts("impossible");
// 否则输出最短距离
else cout << t;
return 0;
}
更多推荐
所有评论(0)