题目描述

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

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

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

【输入格式】

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

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

【输出格式】

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

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

【数据范围】

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

【输入样例】

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

【输出样例】

3

 

由于含有负权边,一定不可以使用Dijkstra。

Bellman-Ford算法的实现方式是每一次都对图中的所有边进行一次松弛操作,对于边(u,v):dist[v] = min(dist[v],dist[u] + w),每一轮都对边进行操作,当某一轮没有边再发生松弛操作即可停止。

在最短路存在的前提下,那么我们每一次的松弛操作都应当让最短路的边数+1,而最短路最多只有n-1条边,故最多循环n-1次,即可得出结果,而每次对边进行松弛时间复杂度是O(m),故总时间复杂度是O(nm)。

Bellman-Ford算法还可以检测图中是否存在负权环,当循环松弛操作n-1次后,第n次操作仍然有边发生了松弛操作,那么就意味着n个点的最短路可以拥有n条边,因此必然存在环,但一般判断负环不使用Bellman-Ford算法,而是用优化后的版本,SPFA算法,包括求最短路也是,那么Bellman-Ford算法还有什么优势呢?优势就在于本题,有边数限制的最短路问题只能用Bellman-Ford算法来求解

需要注意的点:

①由于是每一次对边进行松弛操作,因此存储图的方式可以多变,只要能够每次都遍历完所有边即可,故用结构体存下所有的边会比邻接表书写起来更方便;

②每次松弛的时候,是使用上次松弛完的结果来计算本次的结果,因此计算的时候需要备份一次上次的结果,以免发生“串联更新”的问题,也就是使用本次松弛计算的结果来更新后续的结果;

③输出答案时,可能存在负权边更新了两个无法到达的点的情况,所以判断不能直接判断是否等于0x3f,比如1无法到达点5,也无法到达点7,但5->7的边权是-2,那么在每次松弛的时候,是会导致这个值变小一点点的。

 

 1 #include <iostream>
 2 #include <string.h>
 3 using namespace std;
 4 
 5 const int N = 501,M = 10009;
 6 int n,m,k;
 7 int dist[N],backup[N];
 8 struct Edge
 9 {
10     int a,b,w;
11 }edges[M];
12 
13 void BellmanFord()
14 {
15     memset(dist,0x3f,sizeof dist);
16     dist[1] = 0;
17     for(int i = 0;i < k;++i)//限制k条边则松弛k次
18     {
19         memcpy(backup,dist,sizeof dist);
20         for(int j = 0;j < m;++j)
21         {
22             int a = edges[j].a;
23             int b = edges[j].b;
24             int w = edges[j].w;
25             dist[b] = min(dist[b],backup[a] + w);
26         }
27     }
28     if(dist[n] < 0x3f3f3f3f/2)
29         cout << dist[n] << endl;
30     else
31         cout << "impossible" << endl;
32 }
33 
34 int main()
35 {
36     cin >> n >> m >> k;
37     for(int i = 0;i < m;++i)
38     {
39         int a,b,w;
40         cin >> a >> b >> w;
41         edges[i] = {a,b,w};
42     }
43     
44     BellmanFord();
45     
46     return 0;
47 }