前言
(SPFA) 通常在稀疏图中运行效率高于 (Dijkstra) ,但是也容易被卡。
普通的 (SPFA) 时间复杂度为 (O(km)) ,其中 (k) 是一条边松弛其端点点的次数,是一个较小的常数。
但是对于特殊构造的图中也会退化到 (O(nm)) ,这就与 (Bellman-Ford) 一样。
对此部分情况,可使用 (SLF) 与 (LLL) 优化 (SPFA) 。
C++代码
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f
void Quick_Read(int &N) {
N = 0;
int op = 1;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-')
op = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
N = (N << 1) + (N << 3) + (c ^ 48);
c = getchar();
}
N *= op;
}
const int MAXN = 1e6 + 5;
struct Node {
int to, dist;
Node() {}
Node(int T, int D) {
to = T;
dist = D;
}
};
vector<Node> v[MAXN];
deque<int> q;
int dis[MAXN];
bool inque[MAXN];
int n, m, s;
void Write() {
for(int i = 1; i <= n; i++)
if(dis[i] != INF)
printf("%d ", dis[i]);
else
printf("2147483647 ");
}
void SPFA() {
int iqn = 1, fis = 0;
memset(dis, 0x3f, sizeof(dis));
dis[s] = 0;
memset(inque, 0, sizeof(inque));
inque[s] = true;
q.push_back(s);
while(!q.empty()) {
int now = q.front(); q.pop_front();
inque[now] = false;
fis -= dis[now];
iqn--;
int SIZ= v[now].size();
for(int i = 0; i < SIZ; i++) {
int next = v[now][i].to;
if(dis[next] > dis[now] + v[now][i].dist) {
dis[next] = dis[now] + v[now][i].dist;
if(!inque[next]) {
inque[next] = true;
if(q.empty() || dis[next] > dis[q.front()] || dis[next] * iqn <= fis)
q.push_back(next);
else
q.push_front(next);
fis += dis[next] + v[now][i].dist;
iqn++;
}
}
}
}
}
void Read() {
int A, B, C;
Quick_Read(n);
Quick_Read(m);
Quick_Read(s);
for(int i = 1; i <= m; i++) {
Quick_Read(A);
Quick_Read(B);
Quick_Read(C);
v[A].push_back(Node(B, C));
}
}
int main() {
Read();
SPFA();
Write();
return 0;
}
但上述优化也有缺陷,并不适用与所有图。
(LLL) 优化常见卡掉的方法很简单,向 (1) 连接一条权值巨大的边,这样 (LLL) 也无能为力,之前做的松弛等于白费。
(SLF) 使用链套菊花,可以轻松卡掉。
若全部边权正负性相同,还是使用 (Dijkstra) 算法,其稳定性是 (SPFA) 不具有的。