这段代码实现了一个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;
}

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐