最短路 - Dijkstra 演算法

懒羊羊爱吃灰太狼發表於2024-08-21

Dijkstra(迪傑斯特拉)演算法是基於貪心思想的單源最短路演算法

暴力 Dijkstra

具體如下:

struct node { int v, w; };
vector<node> e[N];
int dist[N], vis[N];

e[u] 存的是節點 u 的所有出邊的終點和邊權,dist[u] 存 u 到原點 s 的最小距離, vis[u] 標記是否走過

void dijkstra(int s) {
	memset(dist, 0x3f, sizeof dist);  // 初始先讓所有點設為 +∞
	dist[s] = 0;  // 原點離自身距離為0
	for(int i = 1; i < n; i ++) {  // 列舉次數
		int u = 0;
		for(int j = 1; j <= n; j ++) {  // 列舉點
			if(!vis[j] && dist[j] < dist[u]) u = j;  // 選擇一個距離最小的標記
		}
		vis[u] = 1;
		for(auto ed : e[u]) {  // 對u的所有出邊,更新鄰邊v的最小距離
			int v = ed.v, w = ed.w;
			if(dist[v] > dist[u] + w) dist[v] = dist[u] + w;
		}
	}
}

主函式:

int main() {
	cin >> n >> m >> s;
	for(int i = 0; i < m; i ++) {
		cin >> a >> b >> c;
		e[a].push_back({b, c});  // 加入a的鄰邊b和權值c
	}
	dijkstra(s);
	return 0;
}

但是以上的方法對於負邊權不適用,會出錯;

並且時間複雜度為 \(O(n^2 + m) ≈ O(n^2)\)

對於 \(n = 10^3, m = 10^6\) AC, 而對於 \(n = 10^6, m = 10^6\)TLE;

Dijkstra 演算法的堆最佳化 ---- 用優先佇列維護被更新的點的集合

Heap - Dijkstra

具體如下:

priority_queue<pair<int, int>> q;

建立一個pair型別的大根堆 q{-距離, 點},或者是 小根堆 q{距離, 點}; 但小根堆需要自己用結構體加過載運算子

小根堆寫法:

點選檢視程式碼
struct node
{
    int first;  // 距離
    int second;  // 點編號
    bool operator <(const node &x)const  // 過載運算子
    {
        return x.first<first;
    }
};

priority_queue<node> q;
void dijkstra(int s) {
	memset(dist, 0x3f, sizeof dist);
	dist[s] = 0;
	q.push({0, s});  // 原點入隊
	while(q.size()) {
		auto t = q.top();
		q.pop();
		int u = t.first;  // 從隊頭彈出距離最小的點u
		if(vis[u]) continue;  //如果走過就直接出隊
		vis[u] = 1;  //沒走過標記下
		for(auto ed : e[u]) {
			int v = ed.v, w = ed.w;
			if(dist[v] > dist[u] + w) {
				dist[v] = dist[u] + w;
				q.push({-dist[v], v});  // 把更小的點及距離入隊
			}
		}
	}
}

總結:

暴力 Dijkstra Heap - Dijkstra
時間複雜度 \(O(n^2)\) \(O(mlogm)\)
圖的大小 稠密圖
\(m = O(n^2)\)
稀疏圖
\(m = O(n)\)
負邊權 不能 不能

相關文章