bellman-ford
题目链接:https://www.acwing.com/problem/content/858/
时间复杂度:O( m ∗ n m*n m∗n)
适用于:单源最短路,存在负边权
算法思想:
外层循环
K
K
K 次,内层循环
M
M
M 次
每次都进行松弛操作
d
i
s
t
[
b
]
=
m
i
n
(
d
i
s
t
[
b
]
,
b
a
c
k
u
p
[
a
]
+
w
)
;
dist[b] = min(dist[b], backup[a] + w);
dist[b]=min(dist[b],backup[a]+w);
注意: b a c k u p [ ] backup[] backup[] 数组是上一次迭代后 d i s t [ ] dist[] dist[] 数组的备份,由于是每个点同时向外出发,因此需要对 d i s t [ ] dist[] dist[] 数组进行备份,若不进行备份会因此发生串联效应,影响到下一个点
为什么要使用backup[]
为了避免如下的串联情况, 在边数限制为一条的情况下,节点3的距离应该是3,但是由于串联情况,利用本轮更新的节点2
更新了节点3的距离,所以现在节点3的距离是2。
正确做法是用上轮节点2更新的距离–无穷大
,来更新节点3, 再取最小值,所以节点3离起点的距离是3。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510,M = 10010;
struct edge{
int a;
int b;
int w;
}edges[M]; // 把每个边保存下来即可
int n,m,k; // k代表最短路径最多包涵k条边
int backup[N],dist[N]; // 备份数组防止串联
void 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++) // 遍历所有边
{
int a,b,w;
a = edges[j].a;
b = edges[j].b;
w = edges[j].w;
dist[b] = min(dist[b],backup[a] + w); // 使用backup:避免给a更新后立马更新b, 这样b一次性最短路径就多了两条边出来
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i = 0; i < m; i++)
{
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
edges[i] = {a,b,w};
}
bellman_ford();
if(dist[n] > 0x3f3f3f3f / 2) printf("impossible\n");
else printf("%d\n",dist[n]);
return 0;
}