图论——最短路算法笔记 djkstra

一. 单源最短路 

1. 边权为正

(1)朴素版dijkstra算法(基于稠密图)

算法原理:维护一个S集合,存放已经确定最短距离的点(从指定起点到当前点),循环n次,每次从S集合外,取出距离最近的点 t,将 t 加进S集合,然后用 t 点,更新起点到其他所有点的距离

 1 // dijkstra朴素版
 2 #include <iostream>
 3 #include <cstring>
 4 #include <cstdio>
 5 #define N 510
 6 using namespace std;
 7 
 8 int g[N][N], dist[N];
 9 bool st[N];
10 int n, m;
11 
12 int dijkstra()
13 {
14     memset(dist, 0x3f, sizeof dist);
15     dist[1] = 0;
16     // st[1] = true; 不能在初始化的时候这样做,因为dist[1]实际上还有待进一步确定
17 
18 
19     for (int i = 0; i < n; i ++) // 循环n次,遍历每个点,每遍历一个点,这个点到1号点的最短距离就唯一确定了
20     {
21         int t = -1; // 用-1初始化t,t最后需要返回的使最近的点的下标
22         for (int j = 1; j <= n; j ++) // 找到st集合以外的点(还未确定到1号点的最短距离)中离1号点最近的点
23         {
24             if(!st[j] && (t == -1 || dist[j] < dist[t])) t = j;
25         }
26 
27         if(t == n) break; // 如果n号点到1号点的距离已经唯一确定了,就可以直接break
28 
29         st[t] = true; // 到这一步t号点到1号点的距离就已经唯一确定了(加入集合st)
30 
31         for (int j = 1; j <= n; j ++) // 用最新确定的t号点来更新所有点到1号点的距离
32         {
33             dist[j] = min(dist[j], dist[t] + g[t][j]); // 在(原值)与(先经过t点再从t点到j点的路径长度)二者之间取最小值
34         }
35     }
36 
37     if(dist[n] == 0x3f3f3f3f) return -1; // 代表1号点与n号点不连通
38     return dist[n];
39 }
40 
41 int main()
42 {
43     cin >> n >> m;
44 
45     memset(g, 0x3f, sizeof g);
46 
47     for (int i = 0; i < m; i ++)
48     {
49         int a, b, c;
50         scanf("%d%d%d", &a, &b, &c);
51         g[a][b] = min(g[a][b], c); // 在重边中保留最短的那一条
52     }
53 
54     int t = dijkstra();
55 
56     printf("%d\n", t);
57 
58     return 0;
59 }

(2)dijkstra算法(堆优化版)

 1 // dijkstra堆优化版
 2 #include <iostream>
 3 #include <cstring>
 4 #include <cstdio>
 5 #include <queue>
 6 #define N 150010
 7 using namespace std;
 8 
 9 int n, m;
10 int idx, h[N], e[N], ne[N], w[N]; // w(weight)记录每条边的权重(边的长度)
11 typedef pair<int, int> PII;
12 int dist[N];
13 bool st[N]; // 标记第i号点到1号点的最短距离是否唯一确定
14 
15 void add(int a, int b, int c)
16 {
17     e[++ idx] = b, ne[idx] = h[a], h[a] = idx, w[idx] = c;
18 }
19 
20 int dijkstra()
21 {
22     memset(dist, 0x3f, sizeof dist);
23     dist[1] = 0;
24 
25     // 用来存储所有的未确定的点及其距离,每次以第一关键字(点到1号点的距离)为排序顺序,返回最小距离(小根堆)
26     // 但是优先队列不能修改某个值,所以每次只能把更新的值push进去,再用一个st数组来记录i号点是否已经确定最短距离
27     priority_queue<PII, vector<PII>, greater<PII>> heap;
28     heap.push({0, 1});
29 
30     while(heap.size()) // 代替朴素版中的每次找到未确定的点中距离1号点最近的点
31     {
32         PII t = heap.top(); // 找出来作为新确定的
33         heap.pop();
34 
35         int id = t.second, distance = t.first;
36 
37         if(id == n) break; // 如果n号点的最短距离已经唯一确定了,就break
38         if(st[id]) continue; // 如果这个点已经是确定过的点
39 
40         st[id] = true;
41         for (int i = h[id]; i != 0; i = ne[i]) // 通过相连的边来更新所有关联的点
42         {
43             int tmp = distance + w[i];
44             int j = e[i];
45             if(st[j]) continue; // 如果是已经确定的点-->有回路
46             if(dist[j] > tmp)
47             {
48                 dist[j] = tmp;
49                 heap.push({tmp, j});
50             }
51         }
52     }
53 
54     if(dist[n] == 0x3f3f3f3f) return -1; // 1号点与n号点不连通
55     return dist[n];
56 }
57 
58 int main()
59 {
60     cin >> n >> m;
61 
62     for (int i = 0; i < m; i ++)
63     {
64         int a, b, c;
65         scanf("%d%d%d", &a, &b, &c);
66         add(a, b, c);
67     }
68 
69     printf("%d\n", dijkstra());
70 
71     return 0;
72 }

 

posted on 2020-04-20 21:46  Victayria  阅读(20)  评论(1)    收藏  举报