與其說是 SPFA 演算法的最佳化,倒不如說是 Bellman-Ford 演算法的最佳化。
棧最佳化
將原本的 bfs 改為 dfs,在尋找負環時可能有著更高效的效率,但是最壞複雜度為指數級別。
void dfs_spfa(int u) {
if (fg) return;
vis[u] = true;
for(pil it : son[u]) {
int v = it.first;
ll w = it.second;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (vis[v] == true) {//如果這個點被訪問過,就說明這是負環
fg = true;//打標記
return;
}
else dfs_spfa(v);
}
}
vis[u] = false;
}
SLF 最佳化
將 queue
換成 deque
,判斷與隊首元素的 dis
的大小,小的就放隊首,大的就放隊尾。
void spfa(int s) {
for(int i = 1; i <= n; ++ i) {
dis[i] = inf;
}
dis[s] = 0;
q.push_back(s);
f[s] = 1;
while (!q.empty()) {
int u = q.front();
q.pop_front();
f[u] = 0;
for (pii it : son[u]) {
int v = it.first;
int w = it.second;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (! f[v]) {
if (! q.empty() && dis[v] < dis[q.front()]) {
q.push_front(v);
}
else q.push_back(v);
f[v] = 1;
}
}
}
}
}
D´Esopo-Pape 最佳化
將 queue
換成 deque
,判斷一個點是否入過佇列,沒入過就放到隊尾,如果就放到隊首。
void spfa(int s) {
for(int i = 1; i <= n; ++ i) {
dis[i] = inf;
}
dis[s] = 0;
q.push_back(s);
f[s] = 1;
vis[s] = 1; // 是否入過隊
while (!q.empty()) {
int u = q.front();
q.pop_front();
f[u] = 0;
for (pii it : son[u]) {
int v = it.first;
int w = it.second;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (! f[v]) {
if (vis[v]) {
q.push_front(v);
}
else {
q.push_back(v);
vis[v] = 1;
}
f[v] = 1;
}
}
}
}
}
LLL 最佳化
將 queue
換成 deque
,每次將入隊結點距離和隊內距離平均值比較,如果更大則插入至隊尾,否則插入隊首。
void spfa() {
ll sum = 0;
for (int i = 1; i <= n; ++ i) {
dis[i] = inf;
}
dis[s] = 0;
q.push_back(s);
g[s] = 1;
sum += dis[s];
while (!q.empty()) {
int u = q.front();
q.pop_front();
vis[u] = false;
sum -= dis[s];
for (pli it : son[u]) {
if (dis[it.second] > dis[u] + it.first) {
dis[it.second] = dis[u] + it.first;
if (! vis[it.second]) {
if (q.empty() || dis[it.second] > sum / ((int)q.size())) {
q.push_back(it.second);
}
else {
q.push_front(it.second);
g[it.second] = 1;
}
vis[it.second] = true;
}
}
}
}
}
SLF 帶容錯最佳化
將 queue
換成 deque
,判斷與隊首元素的 dis
的大小,設定一個值 \(W\),如果比隊首元素大超過 \(W\) 則放隊尾。
\(W\) 一般設為所有邊權的和的開方,即 \(\sqrt{sum}\)。
mcfx 最佳化
在第 \(\left[L, R\right]\) 次訪問一個結點時,將其放入隊首,否則放入隊尾。通常取 \(L = 2, R = \sqrt{\left|V\right|}\)。
SLF + swap 最佳化
每當佇列改變時,如果隊首距離大於隊尾,則交換首尾。