「學習筆記」SPFA 演算法的最佳化

yi_fan0305發表於2023-05-01

與其說是 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 最佳化

每當佇列改變時,如果隊首距離大於隊尾,則交換首尾。

相關文章