算法笔记——Bellman_Ford算法

169 阅读1分钟

「这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战

算法概述

这次主要来讲解Bellman_Ford算法,这是一种专门针对存在负权边的最短路问题,该算法核心是:遍历所有的n个点,然后再找到距离最短的边,用这个点来更新到其他点的距离。

在循环n次后,一定满足次三角不等式:dist[b] <= dist[a] + w

注意:

1、Bellman_Ford算法可以使用邻接表来存储,也可以使用结构体数组进行存储,只要能表示两点之间边的权重即可;

2、当有负权回路时,最短路径不一定会存在

  • 如下,从1到5寻找最短路径时,会陷入负权回路中,在当中转的圈越多,路径数越小,甚至出现负无穷的情况

image.png

  • 当遇到这种负环,负环点不能通往终点,也会有最短路径

image.png

模板题

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数

请你求出从 1 号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出 impossible

注意:图中可能 存在负权回路

输入格式

第一行包含三个整数 n,m,k。

接下来 m 行,每行包含三个整数 x,y,z表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式

输出一个整数,表示从 11 号点到 n 号点的最多经过 k 条边的最短距离。

如果不存在满足条件的路径,则输出 impossible

数据范围

1≤n,k≤500, 1≤m≤10000, 任意边长的绝对值不超过 10000。

输入样例:

3 3 1
1 2 1
2 3 1
1 3 3

输出样例:

3

提示

迭代k次后,最短距离的含义:从1号点出发,最多经过不超过k条边,走到每个点的距离。

判断是否存在环路:存在一条最短路径,上面有n条边,意味着有n+1个点,又由抽屉原理得出一定会有两个重复的点,说明路径上一定会有环。

要使用备用数组来更新距离,因为要满足题目要求,最多不经过k条边找到最短距离。

代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 10010;

int backup[N], dist[N];

struct bellman_ford{
    int a, b, m;
}bell[N];
int n, m, k;

int bellman_Ford()
{
    memset(dist, 0x3f ,sizeof dist);
    dist[1] = 0;
    
    for(int i = 0;i < k;i ++)
    {
        // 进行拷贝
        memcpy(backup, dist, sizeof dist);
        for(int j = 0;j < m;j ++)// 遍历m条边
        {
            int a = bell[j].a, b = bell[j].b, m = bell[j].m;
            dist[b] = min(dist[b], backup[a] + m);
        }
           
        
    }
    
    if(dist[n] > 0x3f3f3f3f / 2) return -2;
    
    return dist[n];
}

int main()
{
    cin >> n >> m >> k;
    for(int i = 0;i < m;i ++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        bell[i].a = x, bell[i].b = y, bell[i].m = z;
    }
    int t = bellman_Ford();
    if(t == -2)
    {
        puts("impossible");
    }
    else
    {
        cout << t;
    }
    return 0;
}