1、概念
SPFA(Shortest Path Faster Algorithm,最短路径更快算法)是 Bellman-Ford 算法的一种队列优化版本,用于求解有向图中单源最短路径问题,可以处理带负权边的图(但不能有负权环)。
负权环是图中一条边权之和为负数的有向回路。两个特点:第一是能回到起点,第二是路径上的边权加起来小于 0。如果图中存在负权环,那么你可以不断走这个环来让路径越来越短。这会导致最短路径算法陷入死循环,所以像 Bellman-Ford 和 SPFA 都需要做负环检测。
2、实战例子
给定n个节点,m条边。包含负权边,重环。
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
int h[N], e[N], w[N], ne[N], idx; // 邻接表
int dist[N], state[N];
int n, m;
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void spfa() {
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
queue<int> q; // 用来存储未计算的点;删除已使用的点;
q.push(1);
state[1] = 1;
while (!q.empty()) {
int u = q.front(); q.pop();
state[u] = 0; // u被使用之后需要删除;因为u在后面可能会被再次用到;
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i], cost = w[i];
if (dist[v] > dist[u] + cost) {
dist[v] = dist[u] + cost;
if (!state[v]) q.push(v), state[v] = 1; //状态设置为1是保证不会被重复插入队列
}
}
}
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m;
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
spfa();
if (dist[n] == 0x3f3f3f3f) cout << "impossible";
else cout << dist[n];
return 0;
}
3、spfa和Dijkstra、Bellman_Ford比较
Dijkstra算法核心采用贪心算法,只能计算正权边的情况;
Bellman_Ford解决了Dijkstra的问题,但是每一次计算都要对所有边进行松弛,计算复杂;
spfa解决了Bellman_Ford的问题,每一次只需要考虑当前节点及相邻节点,计算效率更高。