Thuật toán Floyd tìm đường đi ngắn nhất giữa mọi cặp đỉnh
2017-08-09Bài gốc
Đề bài
Cho đơn đồ thị vô hướng N đỉnh và M cạnh, trọng số các cạnh C đều nguyên dương. Có K câu hỏi thuộc 2 loại: 0 u v : Cho biết đường đi ngắn nhất từ u tới v có độ dài là bao nhiêu. 1 u v : Hãy chỉ ra 1 đường đi ngắn nhất từ u => v.
Giới hạn:
- 1 ≤ N ≤ 100
- 1 ≤ M ≤ N*(N-1)/2
- 1 ≤ K ≤ 1000
- 1 ≤ C ≤ 10000
Định dạng test
Input:
- Dòng 1 : 3 số nguyên N , M , K.
- M dòng tiếp theo , dòng thứ i gồm 3 số nguyên dương u , v , c cho biết cạnh (u,v) có trọng số là c.
- K dòng tiếp theo là K câu hỏi , dòng thứ j sẽ có định dạng như đã nêu ở trên.
Output:
- Ứng với mỗi câu hỏi trong K câu hỏi thì ta phải trả lời trên mỗi dòng như sau .
- Câu hỏi 0 u v : Ghi ra 1 số nguyên duy nhất là độ dài đường đi ngắn nhất từ u -> v.
- Câu hỏi 1 u v : Ghi ra số đầu tiên là số X là số đỉnh trên đường đi ngắn nhất này , tiếp đó ghi ra X số là chỉ số các đỉnh theo thứ tự xuất hiện trên hành trình.
Ví dụ:
Thuật toán
Tìm đường đi ngắn nhất giữa mọi cặp đỉnh sử dụng thuật toán Floyd: gọi w[i][j] là khoảng cách ngắn nhất từ đỉnh i đến j, giá trị khởi tạo:
- w[i][i] = 0.
- w[i][j] = c[i][j].
- w[i][j] = ∞ nếu giữa i và j không có cạnh nối.
Truy vết: gọi trace[i][j] là đỉnh ngay trước j trong đường đi ngắn nhất từ i đến j (i -> trace[i][j] -> j), giá trị khởi tạo:
- trace[i][j] = i.
Source code
Ngôn ngữ: C++.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
const int N = 105;
const ll oo = 1e18;
int n, m, k, trace[N][N];
ll c[N][N];
void tracefnc(int u, int v, vector<int> & path) {
if (u != v) tracefnc(u, trace[u][v], path);
path.pb(v);
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> m >> k;
// floyd init
FOR(i,1,n) FOR(j,1,n) c[i][j] = oo;
FOR(i,1,n) c[i][i] = 0;
FOR(i,1,n) FOR(j,1,n) trace[i][j] = i;
// edges
FOR(i,1,m) {
int u, v;
cin >> u >> v >> c[u][v];
c[v][u] = c[u][v];
}
// floyd
FOR(k,1,n)
FOR(i,1,n)
FOR(j,1,n)
if (c[i][k] + c[k][j] < c[i][j]) {
c[i][j] = c[i][k] + c[k][j];
trace[i][j] = trace[k][j];
}
FOR(i,1,n)
FOR(j,1,n)
if (c[i][j] == oo) trace[i][j] = -1;
// solve queries
while(k--) {
int t, u, v;
cin >> t >> u >> v;
if (t == 0) cout << c[u][v];
else {
if (trace[u][v] == -1) { cout << -1; continue; }
vector<int> path;
tracefnc(u, v, path);
cout << sz(path);
REP(i,sz(path)) cout << ' ' << path[i];
}
if (k) cout << '\n';
}
return 0;
}
Độ phức tạp
O(N3 + KN)