1, Foreword
Dijkstra algorithm and Spfa algorithm are commonly used in the shortest path problem. However, if we learn these two algorithms, we can find that the priority queue optimization of Dijkstra algorithm is very similar to the code of Spfa algorithm. If we do not deeply understand the principles and differences of the two algorithms, it is easy to be confused when writing the code. Therefore, the author hopes to analyze the difference between the two by writing this blog.
Related blog links:
Introduction to Dijkstra algorithm
Introduction to Spfa algorithm
2, Difference analysis of algorithm principle
Key and difficult points: the use of bool array ch[N] and the position determination of if statement
The biggest difference: SPFA algorithm can deal with edges with negative weight, while Dijkstra algorithm can't.
The reason why the codes of the two algorithms are similar is that they both use queue data structure, but the principle difference between the two algorithms is not small. Here is a brief review of the two algorithms
- Dijkstra: first save the length of each edge and the distance between the connected endpoint and each two initialization points as positive infinity. Then, through the cycle, each cycle determines a point i. point i is the point closest to the starting point among all the undetermined points at present, and then enumerates all the undetermined points j to see whether it is close from the previously determined point to point j or from point i to point j.
- SPFA: in Bellman_ In Ford algorithm, we need to traverse each edge in turn (a ----- > b, length w) to update our dis[b]. However, we find that if dis[a] does not change during the cycle, the formula (dis[b]=min(dis[b],te[a]+w)) will not update our dis[b]. We have done a lot of useless operations, which has a great impact on the running speed of the program. Then we need to think like this: can we use a method to record the state of a and update all points starting from a only when dis[a] changes? This is the idea of SPFA algorithm. In terms of time complexity, although the time complexity of SPFA algorithm has the possibility of degradation, it is basically better than Bellman_Ford.
1.Dijkstra analysis
By reading the algorithm principle, in Dijkstra algorithm, the object we focus on maintaining is the point closest to the starting point. Through each cycle, determine the point closest to the starting point at a time, and then use this point to update all undetermined points. Every time we determine a point, we should mark it so that it will not be updated. Since rings may appear in the figure, the determined points may also be push ed into our priority queue. Therefore, after we take out elements from the priority queue every time, we need to judge whether the point has been determined. If it has been determined, we can't use it to update other points, so we continue it.
Core code:
mem(ch, 0); mem(dis, 0x3f); priority_queue<pii ,vector<pii>,greater<pii>>q; dis[k] = 0; q.push({ 0,k }); while (!q.empty()) { auto te = q.top(); q.pop(); int d = te.first,idt=te.second; if (ch[idt])continue; //In dijkstra algorithm, once a point is determined, it can no longer be traversed, so it is necessary to judge before traversing the connected edges ch[idt] = true; for (int i = h[idt]; ~i; i = ne[i]) { int j = e[i]; if (dis[j] > d + w[i]) { dis[j] = d + w[i]; q.push({ dis[j],j }); } } }
2.Spfa analysis
Different from Dijkstra algorithm, Spfa algorithm focuses on the edges connected with points compared with points. In other words, although the point is stored in a queue, the focus of the algorithm is to traverse all edges of the point after taking out the point, and the edge traversal may be more than once in Spfa algorithm. This leads to one point: in Dijkstra algorithm, bool array ch[N] can only be modified once, but Spfa algorithm allows ch[N] to be modified repeatedly for the following reasons:
In Dijkstra algorithm, the ch array records the determined points. The determined points cannot return to the uncertain state, so of course, the ch array cannot be modified.
In Spfa algorithm, the ch array records the points whose distance has been updated. Its definition determines that the ch array can be modified many times because the dis of a point can be modified many times. If a point is already waiting in the queue to update other distances, we don't have to queue another point.
Core code:
mem(ch, 0); mem(dis, 0x3f); queue<int>q; dis[k] = 0; ch[k] = true; q.push(k); while (!q.empty()) { int te = q.front(); q.pop(); ch[te] = false; for (int i = h[te]; ~i; i = ne[i]) { int j = e[i]; if (dis[j] > dis[te] + w[i]) //Even if a point has been updated, it should be compared in the queue { dis[j] = dis[te] + w[i]; //Because dis may be shorter than the last update if (!ch[j]) //In the spfa algorithm, the ch array determines whether the target point is in the queue to avoid repeated queue entry at the same point. Even if it is not in the queue, it can be updated, so it only checks the duplicate before entering the queue { q.push(j); ch[j] = true; } } } }
3, Explanation and analysis of examples
1. Example introduction
Link: Acwing sweet butter
farmer John Found a way to make the sweetest butter in Wisconsin: sugar. Put sugar on a pasture, he knows N A cow will come and lick it so that it can make super sweet butter that can sell well. Of course, he will pay extra for the cows. farmer John Very cunning, like Pavlov before, he knew he could train these cows to go to a specific pasture when they heard the bell. He plans to put the sugar there and ring the bell in the afternoon so that he can milk at night. farmer John Know that every cow is in their favorite pasture (a pasture does not necessarily have only one cow). Give the route of each cow between the pasture and the pasture, and find out the distance to which all the cows reach and the shortest pasture (he will put the sugar there). The data ensure that at least one pasture is connected to the pasture where all cattle are located. Input format first line: Three numbers: number of cows N,Number of pastures P,Number of inter pasture roads C. Second line to second line N+1 that 's ok: 1 reach N The ranch number where the cow is located. The first N+2 Line to N+C+1 Rows: each row has three numbers: connected pastures A,B,Distance between two pastures D,Of course, the connection is two-way. Output format A total of one line, the output cow must walk the minimum distance and. Data range 1≤N≤500, 2≤P≤800, 1≤C≤1450, 1≤D≤255 Input example: 3 4 5 2 3 4 1 2 1 1 3 5 2 3 7 2 4 3 3 4 5 Output example: 8
2. Topic analysis
For any two-point distance, the first reaction is the Floyd algorithm, but when you think about it carefully, the time complexity of O(n3) makes the Floyd algorithm not suitable for solving this problem. Therefore, we change our thinking and think about other problem-solving ideas. Then we find that the time complexity of Dijkstra algorithm (O(nmlogn)) and Spfa (O(nnm)) after n priority queue optimization is sufficient.
3. Correct code
Here are two kinds of code, both can be passed.
//#pragma gcc optimize(2) #include<iostream> #include<iomanip> #include<cstdio> #include<string> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<map> #include<stack> #include<set> #include<bitset> #include<ctime> #include<cstring> #include<list> #define int long long #define ull unsigned long long #define inf 0x3f3f3f3f #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; typedef pair<int, int> pii; const int N = 1e6 + 7; int n, p, c; int cow[1550]; int h[1550], e[3500], ne[3500], w[3500], id = 1; bool ch[1550]; int dis[1250]; void add(int a, int b, int c) { ne[id] = h[a]; h[a] = id; e[id] = b; w[id] = c; id++; } int dijkstra() { int ans = inf; for (int k = 1; k <= p; k++) { mem(ch, 0); mem(dis, 0x3f); priority_queue<pii ,vector<pii>,greater<pii>>q; dis[k] = 0; q.push({ 0,k }); while (!q.empty()) { auto te = q.top(); q.pop(); int d = te.first,idt=te.second; if (ch[idt])continue; //In dijkstra algorithm, once a point is determined, it can no longer be traversed, so it is necessary to judge before traversing the connected edges ch[idt] = true; for (int i = h[idt]; ~i; i = ne[i]) { int j = e[i]; if (dis[j] > d + w[i]) { dis[j] = d + w[i]; q.push({ dis[j],j }); } } } int res = 0; for (int i = 1; i <= n; i++) if(dis[cow[i]]<inf) res += dis[cow[i]]; else { res = inf; break; } if(res<inf) ans = min(ans, res); } return ans; } int spfa() { int ans = inf; for (int k = 1; k <= p; k++) { mem(ch, 0); mem(dis, 0x3f); queue<int>q; dis[k] = 0; ch[k] = true; q.push(k); while (!q.empty()) { int te = q.front(); q.pop(); ch[te] = false; for (int i = h[te]; ~i; i = ne[i]) { int j = e[i]; if (dis[j] > dis[te] + w[i]) { dis[j] = dis[te] + w[i]; if (!ch[j]) //In the spfa algorithm, the ch array determines whether the target point is in the queue to avoid repeated queue entry at the same point. Even if it is not in the queue, it can be updated, so it only checks the duplicate before entering the queue { q.push(j); ch[j] = true; } } } } int res = 0; for (int i = 1; i <= n; i++) if (dis[cow[i]] < inf) res += dis[cow[i]]; else { res = inf; break; } if (res < inf) ans = min(ans, res); } return ans; } void solve() { mem(h, -1); cin >> n >> p >> c; for (int i = 1; i <= n; i++) cin >> cow[i]; for (int i = 1; i <= c; i++) { int x, y, z; cin >> x >> y >> z; add(x, y, z); add(y, x, z); } cout << spfa() << endl; //cout << dijkstra() << endl; } signed main() { std::ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); solve(); return 0; }
Author: Avalon Demerzel, just like my blog. For more knowledge about graph theory and data structure, see the author's column graph theory and data structure