Tìm đường đi ngắn nhất
đã đăng vào 1 tháng 4 năm 2025, 12:07 a.m.
🛣️ Cheatsheet Các Thuật Toán Tìm Đường Đi Ngắn Nhất
📘 Giới thiệu
Ba thuật toán tìm đường đi ngắn nhất phổ biến nhất trong đồ thị là:
- Dijkstra: Tìm đường đi ngắn nhất từ 1 đỉnh đến các đỉnh còn lại, không có cạnh âm.
- Bellman-Ford: Xử lý được cạnh âm, phát hiện chu trình âm.
- Floyd-Warshall: Tìm đường đi ngắn nhất giữa mọi cặp đỉnh.
📌 So sánh nhanh
Thuật toán | Cạnh âm | Chu trình âm | Đồ thị dày | Đồ thị thưa | Độ phức tạp |
---|---|---|---|---|---|
Dijkstra | ❌ | ❌ | ❌ | ✅ | O((V + E) log V) |
Bellman-Ford | ✅ | ✅ | ✅ | ✅ | O(V * E) |
Floyd-Warshall | ✅ | ✅ | ✅ | ❌ | O(V³) |
1. 🚀 Dijkstra (với hàng đợi ưu tiên)
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
const int INF = 1e9;
vector<int> dijkstra(int start, int n, vector<vector<pii>>& adj) {
vector<int> dist(n + 1, INF);
priority_queue<pii, vector<pii>, greater<>> pq;
dist[start] = 0;
pq.push({0, start});
while (!pq.empty()) {
auto [du, u] = pq.top(); pq.pop();
if (du > dist[u]) continue;
for (auto [v, w] : adj[u]) {
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}
}
return dist;
}
✅ Ưu điểm:
- Rất nhanh với đồ thị không có cạnh âm.
- Có thể dùng
set
,priority_queue
hoặcpairing_heap
.
❌ Nhược điểm:
- Không hoạt động đúng với cạnh âm.
2. 🐌 Bellman-Ford
vector<int> bellman_ford(int start, int n, vector<tuple<int, int, int>>& edges) {
const int INF = 1e9;
vector<int> dist(n + 1, INF);
dist[start] = 0;
for (int i = 1; i < n; ++i) {
for (auto [u, v, w] : edges) {
if (dist[u] != INF && dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
}
}
}
// Kiểm tra chu trình âm
for (auto [u, v, w] : edges) {
if (dist[u] != INF && dist[v] > dist[u] + w)
throw runtime_error("Negative cycle detected");
}
return dist;
}
✅ Ưu điểm:
- Hoạt động với cạnh âm.
- Phát hiện chu trình âm.
❌ Nhược điểm:
- Chậm với đồ thị lớn.
3. 🧠 Floyd-Warshall
void floyd_warshall(int n, vector<vector<int>>& dist) {
for (int k = 1; k <= n; ++k)
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (dist[i][k] < 1e9 && dist[k][j] < 1e9)
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
✅ Ưu điểm:
- Tìm đường đi ngắn nhất giữa mọi cặp đỉnh.
- Dễ cài đặt.
❌ Nhược điểm:
- Độ phức tạp O(V³) → chậm với đồ thị lớn.
🧪 Khi nào dùng thuật toán nào?
Tình huống | Thuật toán gợi ý |
---|---|
Đường đi ngắn nhất từ 1 đỉnh, không cạnh âm | Dijkstra |
Đường đi ngắn nhất từ 1 đỉnh, có cạnh âm | Bellman-Ford |
Tìm đường ngắn nhất giữa mọi cặp đỉnh | Floyd-Warshall |
Cần phát hiện chu trình âm | Bellman-Ford / FW |
🔧 Tối ưu khi dùng
Với Dijkstra:
- Sử dụng
priority_queue
hoặcset
để tăng tốc độ. - Khi dùng cho lưới (grid), có thể thay bằng BFS nếu trọng số = 1.
- Sử dụng
Với Bellman-Ford:
- Dừng sớm nếu không có cập nhật sau 1 lượt lặp.
- Dùng để detect chu trình âm trong Competitive Programming.
Với Floyd-Warshall:
- Dùng ma trận
dist[i][j]
vànext[i][j]
để reconstruct đường đi. - Có thể dùng với đồ thị nhỏ (V ≤ 500).
- Dùng ma trận
Nhận xét