Thuật toán Floyd-Warshall tìm kiếm đường đi ngắn nhất trên đồ thị

Cấu trúc dữ liệu và giải thuật

0.0 (0 đánh giá)
Tạo bởi Katsu Cập nhật lần cuối 17:12 07-07-2022 10.022 lượt xem 2 bình luận
Tác giả/Dịch giả: huulam3011 K9
Học nhanh

Danh sách bài học

Thuật toán Floyd-Warshall tìm kiếm đường đi ngắn nhất trên đồ thị

Dẫn nhập

Một trong những thuật toán phổ biến nhất và có ứng dụng gần gũi trong đời sống chính là thuật toán tìm kiếm đường đi ngắn nhất trên đồ thị. Trong những bài học tới, hãy cùng nhau đi tìm hiểu về các thuật toán tìm đường đi ngắn nhất trên đồ thị nhé!

Nội dung

Để có thể tiếp thu bài học này một cách tốt nhất, các bạn nên có những kiến thức cơ bản về:

Trong bài học ngày hôm nay, chúng ta sẽ cùng nhau tìm hiểu về:

  • Các khái niệm quan trọng
  • Thuật toán Floyd-Warshall

Các khái niệm quan trọng

Trước hết, hãy cùng nhau nhắc lại một số khái niệm.

Đường đi giữa hai đỉnh là tập hợp các cạnh liên kết hai đỉnh với nhau.

Độ dài đường đi giữa hai đỉnh là tổng trọng số các cạnh thuộc đường đi giữa hai đỉnh.

Đường đi ngắn nhất giữa hai đỉnh là đường đi giữa hai đỉnh có độ dài nhỏ nhất.

Ví dụ các đường màu xanh và màu cam thể hiện cho đường đi giữa đỉnh 1 và đỉnh 6 trong đó đường màu cam chính là đường đi ngắn nhất và có chiều dài là 10.


Thuật toán Floyd-Warshall

Bài toán đặt ra

Cho đồ thị vô hướng gồm n đỉnh và một ma trận trọng số kích thước n\times n trong đó ô (i, j) thể hiện một đường đi trực tiếp giữa đỉnh i và đỉnh j có trọng số là giá trị ô (i,j). Hãy tính độ dài đường đi ngắn nhất giữa mọi cặp đỉnh trên đồ thị.

Input:

  • Dòng 1: Số nguyên dương n thể hiện cho số đỉnh của đồ thị (n\leq 100)
  • Dòng 2…n+1: Mỗi dòng gồm n số nguyên để biểu diễn ma trận trong đó ô (i, j) thể hiện một đường đi trực tiếp giữa đỉnh i và đỉnh j có trọng số là giá trị ô (i, j).

Output:

  • Ma trận kết quả kích thước n\times n trong đó ô (i, j) thể hiện độ dài đường đi ngắn nhất giữa đỉnh i và đỉnh j

Ví dụ:

Input

Output

4

2 3 0 1

2 4 6 3

2 3 0 4

4 0 2 5

0 1 0 1

2 0 2 3

2 3 0 3

2 0 2 0


Ý tưởng

Ý tưởng của thuật toán Floyd-Warshall (từ đây mình sẽ gọi là Floyd cho ngắn gọn) xuất phát từ nhận xét sau:

Nếu như đường đi ngắn nhất từ đỉnh x đến đỉnh y là x\rightarrow u_{1} \rightarrow ... \rightarrow u_{m} \rightarrow y thì đường đi x\rightarrow u_{1} \rightarrow ... \rightarrow u_{k-1} \rightarrow u_{k} phải là đường đi ngắn nhất giữa đỉnh x và đỉnh u_{k}, đường đi  u_{k} \rightarrow u_{k+1} \rightarrow ... \rightarrow u_{m} \rightarrow y (1\leq k\leq m) phải là đường đi ngắn nhất giữa đỉnh u_{k} và đỉnh y.

Thật vậy, giả sử tồn tại đường đi x \rightarrow v_{1} \rightarrow v_{2} \rightarrow _{\cdot \cdot \cdot } \rightarrow v_{h} \rightarrow u_{k}  ngắn hơn đường đi x\rightarrow u_{1} \rightarrow ... \rightarrow u_{k-1} \rightarrow u_{k}  thì khi đó đường đi ngắn nhất từ đỉnh x đến đỉnh y sẽ là x \rightarrow v_{1} \rightarrow v_{2} \rightarrow _{\cdot \cdot \cdot } \rightarrow v_{h} \rightarrow u_{k} \rightarrow u_{k+1} \rightarrow _{\cdot \cdot \cdot } \rightarrow u_{m} \rightarrow y  (trái với giả thiết đường đi ngắn nhất từ đỉnh x đến đỉnh y là x\rightarrow u_{1} \rightarrow ... \rightarrow u_{m} \rightarrow y).

Ví dụ: Giả sử ta có đường đi ngắn nhất từ đỉnh 1 đến đỉnh 4 là 1\rightarrow 2\rightarrow 3\rightarrow 4. Nếu đường đi 1\rightarrow 2\rightarrow 5\rightarrow 3 lại ngắn hơn đường đi 1\rightarrow 2\rightarrow 3 thì khi đó đường đi ngắn nhất từ đỉnh 1 đến đỉnh 4 phải là 1\rightarrow 2\rightarrow 5 \rightarrow 3\rightarrow 4 (trái với giả thiết ban đầu). Do đó, không tồn tại bất cứ đường đi nào ngắn hơn.

Do đó, ta có thể phân hoạch việc tính độ dài đường đi ngắn nhất từ đỉnh x đến đỉnh y bằng việc tính độ dài đường đi ngắn nhất từ đỉnh x đến đỉnh k và đỉnh k đến đỉnh y. Đây chính là ý tưởng của thuật toán Floyd.


Cách cài đặt

Ta có một mảng dist[u][v] với ý nghĩa là độ dài đường đi nhỏ nhất giữa đỉnh u và đỉnh v. Ban đầu, dist[u][v] có giá trị chính là giá trị của ô (i,j) trong ma trận cho trước.

Đầu tiên, ta sẽ tiến hành duyệt mọi đỉnh k như là đỉnh trung gian. Tiếp theo, ta sẽ duyệt hai đỉnh uv với ý nghĩa là ta sẽ chèn thử đỉnh k vào giữa hai đỉnh này. Nếu như dist[u][v] > dist[u][k] + dist[k][v] thì ta sẽ cập nhật lại dist[u][v].


Code

#include<bits/stdc++.h>
using namespace std;

const int MaxN = 1 + 1e2;

int n, dist[MaxN][MaxN];

int main(){
    freopen("CTDL.inp","r",stdin);
    freopen("CTDL.out","w",stdout);
    cin >> n;
    for(int i = 1 ; i <= n ; ++i)
    for(int j = 1 ; j <= n ; ++j) cin >> dist[i][j];

    for(int k = 1 ; k <= n ; ++k)
    for(int i = 1 ; i <= n ; ++i)
    for(int j = 1 ; j <= n ; ++j)
    dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);

    for(int i = 1 ; i <= n ; ++i){
        for(int j = 1 ; j <= n ; ++j) cout << dist[i][j] << " ";
        cout << endl;
    }

    return 0;
}

Độ phức tạp

Ta thấy thuật toán Floyd sử dụng 3 vòng lặp lồng nhau do đó độ phức tạp ở đây sẽ là O(n)^{3}.


Kết luận

Qua bài này chúng ta đã nắm về Tìm kiếm đường đi ngắn nhất trên đồ thị với thuật toán Floyd.

Bài sau chúng ta sẽ tìm hiểu về Tìm kiếm đường đi ngắn nhất trên đồ thị với thuật toán Dijkstra.

Cảm ơn các bạn đã theo dõi bài viết. Hãy để lại bình luận hoặc góp ý của mình để phát triển bài viết tốt hơn. Đừng quên “Luyện tập – Thử thách – Không ngại khó”.


Tải xuống

Tài liệu

Nhằm phục vụ mục đích học tập Offline của cộng đồng, Kteam hỗ trợ tính năng lưu trữ nội dung bài học Thuật toán Floyd-Warshall tìm kiếm đường đi ngắn nhất trên đồ thị dưới dạng file PDF trong link bên dưới.

Ngoài ra, bạn cũng có thể tìm thấy các tài liệu được đóng góp từ cộng đồng ở mục TÀI LIỆU trên thư viện Howkteam.com

Đừng quên likeshare để ủng hộ Kteam và tác giả nhé!


Thảo luận

Nếu bạn có bất kỳ khó khăn hay thắc mắc gì về khóa học, đừng ngần ngại đặt câu hỏi trong phần bên dưới hoặc trong mục HỎI & ĐÁP trên thư viện Howkteam.com để nhận được sự hỗ trợ từ cộng đồng.

Nội dung bài viết

Tác giả/Dịch giả

Mình là Nguyễn Hữu Lâm, một người có niềm đam mê rất lớn đối với lập trình. Hiện tại, mình đang là sinh viên Khoa học máy tính của Đại học Bách Khoa Hà Nội. Mong muốn của mình là có thể chia sẻ những kiến thức mà bản thân có cho mọi người, học hỏi, kết bạn với tất cả những người có cùng đam mê với mình.


K9

Nhà sáng lập Howkteam.com, KQuiz.vn & tác giả các khóa học C#, Auto, Unity3D, Python....

Với mong muốn mang đến kiến thức chất lượng, miễn phí cho mọi người, với tâm huyết phá bỏ rào cản kiến thức từ việc giáo dục thu phí. Tôi đã cùng đội ngũ Kteam đã lập nên trang website này để thế giới phẳng hơn.
Hãy cùng chúng tôi lan tỏa kiến thức đến cộng đồng! 

Khóa học

Cấu trúc dữ liệu và giải thuật

Bạn đã từng đau đầu với các cấu trúc stack, queue,.. hoặc cảm thấy cực kỳ khó khăn với các thuật toán sắp xếp, tìm kiếm được sử dụng trong lập trình. Đừng lo lắng! Trong khoá học này, chúng ta sẽ cùng nhau tìm hiểu một cách đơn giản nhất về cấu trúc dữ liệu và giải thuật, cũng như giúp bạn nắm rõ hơn về các kiến thức này.

Hãy cùng xem cấu trúc dữ liệu và giải thuật có gì đáng sợ không nhé!

Đánh giá

Bình luận

Để bình luận, bạn cần đăng nhập bằng tài khoản Howkteam.

Đăng nhập
hiếu lo đc đã bình luận 20:26 20-09-2024

hey creator. i've just tried running your code, but my output isn't alike your output. when i illustrated, way nodes not be likely your output

 
AlanNguyen đã bình luận 10:31 16-03-2024

Bỏ qua trường hợp k = j và k = i nữa thì độ phức tạp giảm hơn nữa

Không có video.