Dijkstra algorithm
For a graph with positive edge weight, we can use Dijkstra algorithm to find single source shortest path (SSSP). For conventional Dijkstra algorithm, its complexity is O(n2)O(n^2)O(n2). Obviously, when nnn is large, it may take too long. By optimizing, we can get a faster Dijkstra algorithm. The inter-complexity is O(mlogn)O(mlogn)O(mlogn). The code for this blog is derived from "Introduction to Algorithmic Competition Classics (2nd Edition)" (Purple Book).
Optimizing Thought
In the traditional Dijkstra algorithm, searching for the least unvisited d[i] i n d[n] is done by traversing the array, which will lead to higher time complexity. Since it is the output of the smallest weight edge, we can use priority_queue in STL to achieve this operation, which can achieve acceleration. . Of course, in order to achieve this operation, instead of using simple adjacency matrix, we store edge data first, and then store edge data in vector array, which is a bit like adjacency table, but not adjacency table.
Code
struct Edge { int from, to, dist; Edge(int u,int v,int d):from(u),to(v),dist(d){} }; struct HeapNode{ int d, u; bool operator <(const HeapNode & rhs) const{ return d > rhs.d; //Instead of the operator definition above, we use > to make the smallest value in the priority queue pop first. } }; struct Dijkstra { int n, m; vector<Edge> edges; vector<int> G[maxn]; bool done[maxn]; int d[maxn]; int p[maxn]; void init(int n){ this->n = n; for (int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void AddEdge(int from, int to, int dist){ edges.push_back(Edge(from, to, dist)); m = edges.size(); G[from].push_back(m - 1); } void dijkstra(int s){ priority_queue<HeapNode> Q; for (int i = 0; i < n;i++) d[i] = INF; d[s] = 0; memset(done, 0, sizeof(done)); Q.push((HeapNode){0, s}); while(!Q.empty()){ HeapNode x = Q.top(); Q.pop(); int u = x.u; if(done[u]) continue; done[u] = true; for (int i = 0; i < G[u].size();i++){ Edge &e = edges[G[u][i]]; if(d[e.to] > d[u] + e.dist){ d[e.to] = d[u] + e.dist; p[e.to] = G[u][i]; Q.push((HeapNode){d[e.to], e.to}); } } } } };
Example
After reading the above code, you might as well try some questions.
Topic: J-free: https://ac.nowcoder.com/acm/contest/884/J
Question: Given a graph with nnn points and mmm edges, the weight of each edge is lll, you can change the weight of up to kkk edges to 000. After such operation, what is the shortest path from vertex SSS to vertex TTT?
Solution: After understanding the above Dijkstra algorithm on heap optimization, it is very helpful to solve this problem. Since the distance to the point is related to kkk, we can no longer store the shortest path of the distance to each point with the original one-dimensional array d[n], but open a two-dimensional array dis[n][m], where dis[i][j] represents the shortest path from the source point to the vertex iii when the weight of the JJ edges is changed to 000. So we can easily get the answer. So how to build this two-dimensional array dis[n][m]? In the original Dijkrstra algorithm, we push a HeapNode to Q under the condition that D [e.to] > D [u] + e.dist. Because there is an additional factor kkk, we need to add a variable ktime to our HeapNode to represent the number of deleted weight edges. At the same time, the criterion of push has changed from one to two. One is whether to push when ktime is unchanged, the other is whether to push when ktime+1 is unchanged. In this way, the dis[n][m] of the two-dimensional array can be constructed perfectly. Finally, when searching for ansansans, you only need to traverse dis[T].
Code:
#include <bits/stdc++.h> using namespace std; const int maxn = 1e3 + 5; typedef long long ll; #define INF 1e9 + 5 int n, m, S, T, k; ll dis[maxn][maxn]; struct Edge { int from, to, dist; Edge(int u,int v,int d):from(u),to(v),dist(d){} }; struct HeapNode{ int d, u, ktime; bool operator<(const HeapNode &rhs) const { return d > rhs.d; } }; struct Dijkstra { int n, m; vector<Edge> edges; vector<int> G[maxn]; bool done[maxn][maxn]; void init(int n){ this->n = n; for (int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void AddEdge(int from, int to, int dist){ edges.push_back(Edge(from, to, dist)); m = edges.size(); G[from].push_back(m - 1); } void dijkstra(int s){ memset(dis, INF, sizeof(dis)); priority_queue<HeapNode> Q; dis[s][0] = 0; memset(done, 0, sizeof(done)); Q.push((HeapNode){0, s, 0}); while (!Q.empty()) { HeapNode x = Q.top(); Q.pop(); int u = x.u; int ktime = x.ktime; if (done[u][ktime]) continue; done[u][ktime] = true; for (int i = 0; i < G[u].size();i++){ Edge &e = edges[G[u][i]]; if(dis[u][ktime]+e.dist<dis[e.to][ktime]){ dis[e.to][ktime] = dis[u][ktime] + e.dist; Q.push((HeapNode){dis[e.to][ktime], e.to, ktime}); } if(ktime<k&&dis[u][ktime]<dis[e.to][ktime+1]){ dis[e.to][ktime + 1] = dis[u][ktime]; Q.push((HeapNode){dis[e.to][ktime + 1], e.to, ktime + 1}); } } } } }; int main(){ scanf("%d%d%d%d%d", &n, &m, &S, &T, &k); Dijkstra dij; dij.init(n); int a, b, l; for (int i = 0; i < m; i++) { scanf("%d%d%d", &a, &b, &l); dij.AddEdge(a, b, l); dij.AddEdge(b, a, l); } dij.dijkstra(S); ll ans = INF; for (int i = 0; i <= k; i++) { ans = (ans < dis[T][i]) ? ans : dis[T][i]; } printf("%lld", ans); return 0; }