「这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战」
算法概述
这次主要来讲解Bellman_Ford算法,这是一种专门针对存在负权边的最短路问题,该算法核心是:遍历所有的n个点,然后再找到距离最短的边,用这个点来更新到其他点的距离。
在循环n次后,一定满足次三角不等式:dist[b] <= dist[a] + w
注意:
1、Bellman_Ford算法可以使用邻接表来存储,也可以使用结构体数组进行存储,只要能表示两点之间边的权重即可;
2、当有负权回路时,最短路径不一定会存在
- 如下,从1到5寻找最短路径时,会陷入负权回路中,在当中转的圈越多,路径数越小,甚至出现负无穷的情况
- 当遇到这种负环,负环点不能通往终点,也会有最短路径
模板题
给定一个 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;
}