Tải bản đầy đủ (.pdf) (48 trang)

Tìm hiểu thuật toán tối ưu trên đồ thị và hướng ứng dụng (tt)

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.51 MB, 48 trang )

LỜI CẢM ƠN
Để hoàn thành tốt được đề tài Nghiên cứu khoa học này là nhờ sự hướng dẫn và
giúp đỡ của giảng viên Ts. Hoàng Văn Dũng. Em xin chân thành cảm ơn sự giúp đỡ
của thầy.
Em cũng rất cảm ơn các thầy cô trong khoa Kỹ thuật – Công nghệ thông tin
trường Đại học Quảng Bình đã tận tình giảng dạy, truyền đạt những kiến thức quý báu
và tạo điều kiện cho em hoàn thành báo cáo này.
Mặc dù đã cố gắng rất nhiều để hoàn thành đề tài với tất cả sự nổ lực của bản
thân, nhưng không sao tránh khỏi những thiếu sót. Em rất mong nhận được sự ủng hộ
và giúp đỡ của các thầy cô và các bạn.
Quảng Bình, ngày 20 tháng 05 năm 2017
Sinh viên thực hiện

Phạm Tuấn Vũ


MỞ ĐẦU ....................................................................................................... 1
1. Lý do chọn đề tài ....................................................................................... 1
2. Mục tiêu nghiên cứu .................................................................................. 1
3. Nội dung nghiên cứu .................................................................................. 1
4. Đối tượng, phạm vi nghiên cứu ................................................................. 1
5. Phương pháp nghiên cứu ........................................................................... 1
CHƯƠNG 1: ĐẠI CƯƠNG VỀ LÝ THUYẾT ĐỒ THỊ .............................. 3
1.1. Một số khái niệm liên quan đến đồ thị.................................................... 3
1.1.1. Định nghĩa đồ thị ................................................................................. 3
1.1.2. Các loại đồ thị ...................................................................................... 3
1.2. Đồ thị con, đồ thị bộ phận và tính liên thông trong đồ thị ...................... 3
1.2.1. Đồ thị con, đồ thị bộ phận.................................................................... 3
1.2.2. Tính liên thông trong đồ thị ................................................................. 3
1.3. Đồ thị Euler, đồ thị nửa Euler và đồ thị Hamilton .................................. 4
1.3.1. Đồ thị Euler và đồ thị nửa Euler .......................................................... 4


1.3.2. Đồ thị Hamilton ................................................................................... 8
1.4. Biểu diễn đồ thị trên máy tính ................................................................ 8
1.4.1. Biểu diễn bằng ma trận kề, ma trận trọng số: ...................................... 8
1.4.2. Danh sách cạnh .................................................................................... 9
1.4.3. Danh sách kề ........................................................................................ 9
CHƯƠNG 2: GIỚI THIỆU MỘT SỐ THUẬT TOÁN TRÊN ĐỒ THỊ ..... 12
2.1. Thuật toán tìm kiếm trên đồ thị ............................................................ 12
2.1.1. Tìm kiếm theo chiều sâu trên đồ thị .................................................. 12
2.1.2. Tìm kiếm theo chiều rộng trên đồ thị ................................................ 12
2.1.3. Tìm đường đi và kiểm tra tính liên thông .......................................... 14
2.2. Đồ thị có trọng số và bài toán tìm đường đi ngắn nhất ........................ 14
2.2.1. Đường đi ngắn nhất trong đồ thị không có trọng số. ......................... 14
2.2.2. Tìm đường đi ngắn nhất ..................................................................... 15
2.2.3. Thuật toán Ford – Bellman ................................................................ 15
2.2.4. Thuật toán Dijkstra ............................................................................ 15
2.2.5. Thuật toán Floyd – đường đi ngắn nhất giữa tất cả các cặp đỉnh. ..... 16
CHƯƠNG 3: LUỒNG CỰC ĐẠI VÀ HƯỚNG ỨNG DỤNG ................. 18
3.1. Giới thiệu luồng cực đại........................................................................ 18
3.1.1. Bài toán luồng trên mạng. .................................................................. 18
3.1.2. Bài toán luồng cực đại trong mạng .................................................... 18
3.1.3. Mạng, luồng trong mạng, bài toán luồng cực đại ............................. 18
3.2. Ứng dụng bài toán luồng cực đại trong mạng ..................................... 19


3.3. Một số bài toán tối ưu tổ hợp ứng dụng từ bài toán luồng ................... 25
3.3.1. Bài toán về thủy lợi ............................................................................ 26
3.3.2. Bài toán về hệ thống đại diện chung .................................................. 27
3.3.3. Về một bài toán tối ưu rời rạc. ........................................................... 27
3.4. Kết luận ................................................................................................. 28
CHƯƠNG 4: MÔ PHỎNG THUẬT TOÁN ............................................... 29

4.1. Phát biểu bài toán .................................................................................. 29
4.2. Yêu cầu bài toán ................................................................................... 30
4.3. Cấu trúc chương trình ........................................................................... 31
4.4. Hoạt động của chương trình.................................................................. 33
4.4.1. Chức năng các file class trong chương trình ..................................... 34
4.4.2. Trình tự hoạt động .............................................................................. 35
KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN ................................................... 36
TÀI LIỆU THAM KHẢO ........................................................................... 37
PHỤ LỤC..................................................................................................... 38


DANH MỤC TỪ VIẾT TẮT:
Tiếng việt:
Từ viết tắt
NNLT

Diễn giải
Ngôn ngữ lập trình

Tiếng anh:
Từ viết tắt
Linked Adjancency List
Strongly connected
Depth first search (DFS)
BFS

Diễn giải
Danh sách kề liên kết
Liên thông mạnh
Tìm kiếm sâu

Tìm kiếm theo chiều rộng


DANH MỤC HÌNH ẢNH
Hình 1.1. Đồ thị liên thông, đồ thị không liên thông ..................................... 4
Hình 1.2. Liên thông mạnh và liên thông yếu ............................................... 4
Hình 1.3. Bảy cây cầu trên sông Pregel ......................................................... 5
Hình 1.4. Đồ thị biểu diễn thành phố Konigsberg ......................................... 5
Hình 1.5. Đồ thị Euler và đồ thị nửa Euler .................................................... 6
Hình 1.6. Đường đi Hamilton ........................................................................ 8
Hình 1.7.Đồ thị vô hướng G và Đồ thị có hướng G1 ..................................... 9
Hình 4.1. Cấu trúc chương trình .................................................................. 31
Hình 4.2. Giao diện của ứng dụng .............................................................. 32
Hình 4.3. Giao diên chạy ứng dụng ............................................................. 32
Hình 4. 4. Vẽ điểm ....................................................................................... 33
Hình 4.5. Vẽ đường...................................................................................... 34
Hình 4.6. Các chức năng trong chương trình ............................................... 35


MỞ ĐẦU
1. Lý do chọn đề tài
Trong những năm trở lại đây, cùng với sự ra đời của máy tính điện tử và sự phát
triển nhanh chóng của Tin học, lý thuyết đồ thị càng được quan tâm đến nhiều hơn.
Đặc biệt là các thuật toán trên đồ thị đã có nhiều ứng dụng trong nhiều lĩnh vực khác
nhau như: Mạng máy tính, Lý thuyết mã, Tối ưu hoá, Kinh tế học v.v... Nó trả lời cho
câu hỏi: Hai máy tính trong mạng có thể liên hệ được với nhau hay không? Hay vấn đề
phân biệt hai hợp chất hoá học có cùng công thức phân tử nhưng lại khác nhau về công
thức cấu tạo cũng được giải quyết nhờ mô hình đồ thị.
Lý thuyết đồ thị không những có nhiều ứng dụng trong thực tế mà còn là công cụ
đắc lực cho ngành công nghệ thông tin. Nó giúp cho chúng ta mô tả một cách dễ dàng

các bài toán phức tạp cụ thể, để từ đó ta có thể mã hoá các bài toán đó vào máy tính.
Hiện nay có nhiều tài liệu, sách, giáo trình đã viết về lý thuyết đồ thị với những
nội dung, đầy đủ giúp cho những người muốn nghiên cứu về lý thuyết đồ thị tham
khảo. Tuy nhiên hầu hết các tài liệu đều chỉ nghiên cứu về lý thuyết và xây dựng các
thuật toán chung cho các bài toán mà chưa có nhiều tài liệu viết về các ứng dụng các
thuật toán để giải bài toán ứng dụng cụ thể. Để hiện thực hóa trong việc học tập, giảng
dạy Tin học ở các trường Trung học phổ thông cũng như các trường Đại học, Cao
đẵng thì luận văn “Tìm hiểu thuật toán tối ưu trên đồ thị và hướng ứng dụng ” là
thực sự cần thiết.
2. Mục tiêu nghiên cứu
Mục tiêu chính của đề tài là nghiên cứu về lý thuyết về đồ thị và một số thuật toán
ứng dụng đồ thị vào học tập cũng như cuộc sống.
3. Nội dung nghiên cứu
- Chương I: Tổng quan về lý thuyết đồ thị
- Chương II: Một số bài toán tối ưu trên đồ thị.
- Chương III: Luồng cực đại và các hướng ứng dụng.
- Chương IV: Mô phỏng thuật toán
4. Đối tượng, phạm vi nghiên cứu
- Đối tượng nghiên cứu: Lý thuyết về đồ thị và ứng dụng đồ thị.
- Phạm vi nghiên cứu: Lý thuyết đồ thị, ứng dụng, xây dựng, lập trình mô phỏng
và hệ thống hóa ứng dụng các thuật toán liên quan đến đồ thị nhằm phục vụ cho việc
học tập cũng như việc giảng dạy trong nhà trường.
5. Phương pháp nghiên cứu
- Phương pháp nghiên cứu lý thuyết:
+ Nghiên cứu lý thuyết về đồ thị, các thuật toán ứng dụng của đồ thị.
+ Hệ thống hóa một số ứng dụng của đồ thị.
1


- Phương pháp nghiên cứu thực nghiệm:

Sử dụng phương pháp nghiên cứu lý thuyết kết hợp với nghiên cứu thực nghiệm:

+ Nghiên cứu, tìm hiểu ứng dụng liên quan đến đề tài trong thực tế.
+ Viết chương trình cho bài toán ứng dụng cụ thể.

2


CHƯƠNG 1: ĐẠI CƯƠNG VỀ LÝ THUYẾT ĐỒ THỊ
1.1. Một số khái niệm liên quan đến đồ thị
1.1.1. Định nghĩa đồ thị
Định nghĩa: Đồ thị là một cấu trúc rời rạc bao gồm các đỉnh và các cạnh nối các
đỉnh của đồ thị. Các loại đồ thị khác nhau được phân biệt bởi kiểu và số lượng cạnh
nối hai đỉnh nào đó của đồ thị [2].
- Nếu cạnh u = (x,y) mà x và y là hai đỉnh phân biệt thì ta nói x, y là hai đỉnh kề
nhau.
- Nếu u = (x,x) thì u là cạnh có hai đỉnh trùng nhau ta gọi đó là một khuyên
- Nếu u = (x,y) mà x,y là cặp đỉnh có phân biệt thứ tự hay có hướng từ x đến y thì
u là một cung, khi đó x là gốc còn y là ngọn hoặc x là đỉnh ra, y là đỉnh vào.
- Khi giữa cặp đỉnh (x, y) có nhiều hơn một cạnh thì ta nói những cạnh cùng cặp
đỉnh là những cạnh song song hay là cạnh bội.
1.1.2. Các loại đồ thị
a. Đồ thị vô hướng
Đồ thị G=<X,U> được gọi là đồ thị vô hướng nếu tất cả các cạnh u ε U mà cặp
đỉnh thuộc nó u = (x,y) (trong đó x,y ε X) không phân biệt thứ tự. Đồ thị vô hướng là
đồ thị không có bất kỳ một cung nào [2].
b. Đồ thị có hướng
Đồ thị G = <X, U> được gọi là đồ thị có hướng nếu tất cả các cạnh u ϵ U mà cặp
đỉnh thuộc nó u = (x, y) (trong đó x,y ε X) có
phân biệt thứ tự. Đồ thị có hướng là đồ thị mà mọi u=(x, y) ε X đều là cung [2].

1.2. Đồ thị con, đồ thị bộ phận và tính liên thông trong đồ thị
1.2.1. Đồ thị con, đồ thị bộ phận
Cho đồ thị G = <X,U>
- Nếu trong đồ thị đó ta bỏ đi một số đỉnh nào đó và các cạnh(cung) xuất phát từ
đỉnh đó thì phần còn lại của đồ thị được gọi là đồ thị con của đồ thị G đã cho
- Nếu trong đồ thị G ta bỏ đi một số cạnh nhưng giữ nguyên các đỉnh thì phần còn
lại của đồ thị được gọi là đồ thị bộ phận của đồ thị G.
1.2.2. Tính liên thông trong đồ thị
1.2.2.1. Tính liên thông trong đồ thị vô hướng
Định nghĩa: Một đồ thị được gọi là liên thông nếu có đường đi giữa mọi cặp
đỉnh phân biệt của đồ thị. Ngược lại, đồ thị này được gọi là không liên thông [2].
G: liên thông;
H: không liên thông

3


G

H

Hình 1.1. Đồ thị liên thông, đồ thị không liên thông
Cho đồ thị G=(V,E) và v V. V' là tập hợp các đỉnh của V liên thông với v, E' là
tập hợp các cạnh nối 2 đỉnh của V'. Khi đó đồ thị G' = (V',E') gọi là thành phần liên
thông (connected component) của G chứa v. Đương nhiên nếu v và u liên thông trong
G thì thành phần liên thông của G chứa v cũng là thành phần liên thông chứa u.
Định lý: Đồ thị G=(V, E) là liên thông khi và chỉ khi G có duy nhất một thành
phần liên thông [2].
1.2.2.2. Tính liên thông trong đồ thị có hướng
G gọi là liên thông mạnh nếu luôn tồn tại đường đi (theo các cung định hướng)

giữa hai đỉnh bất kỳ của đồ thị, g gọi là liên thông yếu nếu đồ thị vô hướng nền của nó
là liên thông

Hình 1.2. Liên thông mạnh và liên thông yếu
Định lý: Nếu trong đồ thị G=(V, E) có đúng hai đỉnh bậc lẻ thì hai đỉnh này phải
liên thông với nhau [2].
Định lý: (Định lý về điều kiện cần và đủ của một đồ thị lưỡng phân)
Đồ thị G = (V, E) là một đồ thị lưỡng phân khi và chỉ khi mọi chu trình của nó đều
có độ dài chẵn [2].
1.3. Đồ thị Euler, đồ thị nửa Euler và đồ thị Hamilton
1.3.1. Đồ thị Euler và đồ thị nửa Euler
Xét bài toán 7 cây cầu ở Konigsberg [3]
Thành phố Konigsberg thuộc nước Cộng hoà Litva có con sông Pregel chảy qua,
giữa sông có cù lao Kneiphof tạo nên bốn vùng đất. Người ta đã xây dựng 7 cây cầu để
nối các vùng đất này lại với nhau.

4


Năm 1736 L. Euler, nhà toán học người Thuỵ Sĩ đã đưa ra bài toán sau đây: Hỏi
có thể đi qua cả 7 cây cầu, mỗi cầu chỉ đi qua một lần rồi quay trở về đúng chỗ xuất
phát được hay không?
Bài toán đã làm say mê cư dân của thành phố. Họ háo hức đi thử nhưng không
thành công. Sau đó, chính L. Euler đã chứng minh được rằng bài toán trên là không
giải được.

Hình 1.3. Bảy cây cầu trên sông Pregel
Nếu biểu diễn mỗi vùng đất: A, B, C, D bằng đỉnh của một đa đồ thịvô hướng và
có cạnh nối giữa chúng nếu có cầu nối tương ứng, thì bài toán trên đưa về việc tìm một
chu trình đi qua mỗi cạnh của đồ thị đúng một lần.

Hình 1.4. Đồ thị biểu diễn thành phố Konigsberg

Trong lý thuyết đồ thị, cho đồ thị G=(V,E), trong đó V là tập hợp các đỉnh, E là
tập hợp các cạnh. Từ bài toán trên, người ta đã đưa ra các khái niệm, định nghĩa về chu
trình và đường đi Euler.
- Đường đi Euler là đường đi, đi qua tất cả các cạnh của đồ thị, mỗi cạnh đúng một
lần trong đó điểm đầu và điểm cuối không trùng nhau.
- Chu trình Euler là chu trình đi qua tất cả các cạnh của đồ thị, mỗi cạnh đúng một
lần trong đó đỉnh đầu và đỉnh cuối trùng nhau (đồ thị có thể có các đỉnh cô lập).
5


- Đồ thị gọi là đồ thị Euler khi nó chứa chu trình Euler, và được gọi là nửa Euler
khi nó chứa đường đi Euler.
- Đối với các đồ thị có hướng, các thuật ngữ đường đi và chu trình được thay bằng
đường đi có hướng và chu trình có hướng.
Một đồ thị là Euler thì sẽ là nửa Euler, điều ngược lại là không đúng.

Hình 1.5. Đồ thị Euler và đồ thị nửa Euler
Từ các định nghĩa trên, ta có điều kiện cần và đủ cho sự tồn tại của chu trình vô
hướng Euler như sau:
Định lý 1 (Định lý Euler): Đồ thị G có chu trình Euler khi và chỉ khi G liên thông
và mọi đỉnh có bậc chẵn khác 0 [2].
(): Giả sử G có chu trình Euler và v là đỉnh bất kỳ của G. Khi đó chu trình
Euler đến v theo cạnh e thì ra khỏi v bằng cạnh e’  e. Do đó bậc của v phải là số chẵn.
G hiển nhiên là liên thông.
(): Giả sử G liên thông và mọi đỉnh có bậc chẵn khác 0. Ta chứng minh G có
chu trình Euler quy nạp theo số cạnh m của G.
m = 1: Vì G liên thông và mọi đỉnh bậc chẵn nên G chỉ có 1 đỉnh và 1 khuyên.
Khuyên đó cũng tạo thành chu trình Euler.

Giả sử G có m cạnh, số đỉnh n > 0 và mọi đồ thị liên thông có số cạnh nhỏ hơn m
với mọi đỉnh bậc chẵn đều có chu trình euler.
+ Trường hợp n = 1 hoặc 2 thì hiển nhiên tồn tại chu trình Euler.
+ Trường hợp n > 2. Vì bậc của các đỉnh chẵn  2, bao giờ cũng chọn được 3 đỉnh
a, b, c với các cạnh x = (a,b), y = (a,c).
Giả sử G chứa cạnh z = (b, c).
Xét đồ thị G’ thu được từ G bằng cách loại bỏ ba cạnh x, y, z. Sẽ xảy ra 1 trong ba
khả năng sau:
G’ liên thông. Vì số cạnh của G’ nhỏ hơn m và các đỉnh vẫn có bậc chẵn nên theo
giả thiết quy nạp tồn tại chu trình Euler C’ của G’. Nối chu trình con (x, y, z) với C’ ta
thu được chu trình Euler C của G.
G’ có 2 thành phần liên thông G1 và G2. Không mất tính tổng quát giả sử G1 chứa
a, G2 chứa b và c. G1 có chu trình Euler C1, G2 có chu trình Euler C2. Ta xây dựng
chu trình Euler C của G như sau. Xuất phát từ đỉnh a đi theo chu trình C1 quay về a,
6


sau đó đi theo cạnh x = (a, b) đến đỉnh b, từ b đi theo chu trình C2 quay về b, sau đó đi
theo cạnh z = (b, c) và y = (c, a) quay về a.
G’ có 3 thành phần liên thông G1, G2 và G3. Không mất tính tổng quát giả sử G1
chứa a, G2 chứa b và G3 chứa c. G1 có chu trình Euler C1, G2 có chu trình Euler C2,
G3 có chu trình Euler C3. Ta xây dựng chu trình Euler C của G như sau. Xuất phát từ
đỉnh a đi theo chu trình C1 quay về a, sau đó đi theo cạnh x= (a, b) đến đỉnh b, từ b đi
theo chu trình C2 quay về b, sau đó đi theo cạnh z= (b, c) đến đỉnh c, từ c đi theo chu
trình C3 quay về c, sau đó đi theo cạnh y= (c, a) quay về a.
Giả sử G không chứa cạnh z = (b, c)
Xét đồ thị G’ thu được từ G bằng cách loại bỏ 2 cạnh x, y và thêm cạnh z. Sẽ xảy
ra 1 trong hai khả năng sau:
G’ liên thông. Vì số cạnh của G’ nhỏ hơn m và các đỉnh vẫn có bậc chẵn nên theo
giả thiết quy nạp tồn tại chu trình Euler C’ của G’. Thay cạnh z C’ bằng cạnh x và y

ta thu được chu trình Euler C của G.
G’ có 2 thành phần liên thông G1 và G2. Không mất tính tổng quát giả sử G1 chứa
a, G2 chứa b và c. G1 có chu trình Euler C1, G2 có chu trình Euler C2. Ta xây dựng
chu trình Euler C của G như sau. Thay cạnh zC2 bằng các cạnh x và y ta có chu
trình C2’. Nối C2’ với C1 ta thu được chu trình Euler C của G.
Định lý 2: Cho đồ thị G có k đỉnh bậc lẻ. Khi đó số đường đi tối thiểu phủ G là
k/2.
Ta đã biết số đỉnh bậc lẻ là chẵn, k=2n. Chứng minh quy nạp theo n [3].
n=1: Nối 2 đỉnh bậc lẻ với nhau bằng cạnh z ta thu được đồ thị G’ thoả định lý
Euler. Như vậy G’ có chu trình Euler C’. Bỏ cạnh z trên C’ ta thu được đường đi Euler
phủ G.
Giả sử G có số đỉnh bậc lẻ là 2n và định lý đúng với k<2n. Nối 2 đỉnh bậc lẻ a, b
nào đó với nhau bằng cạnh z ta thu được đồ thị G’ có 2n-2 đỉnh bậc lẻ. Theo giả thiết
quy nạp G’ có n-1 đường đi phủ G’. Gọi P là đường đi qua cạnh z. Hiển nhiên a, b
không phải đỉnh đầu hoặc cuối của P, vì vậy nếu bỏ cạnh z ta thu được 2 đường đi P1
và P2 cùng với n-2 đường đi còn lại phủ đồ thị G.
Bây giờ xét đồ thị có hướng G = (V, A). Ký hiệu:
R = {u  V : dI(v) = dO(v)}
S = {u  V : dI(v) > dO(v)}
T = {u  V : dI(v) < dO(v)}
Từ bổ đề bắt tay ta có:
d I (v)  dO (v)  dO (v)  d I (v)
d O (v )
d I (v )



vV
v
V

v

S
=

= vT
Ta ký hiệu:

 d (v)  d
vS

I

O

(v ) 

 d
vT

O

(v )  d I (v ) 

k=
=
Đồ thị G1 là đồ thị Euler vì nó có chu trình Euler a - e - c - d - e - b - a.
7



Đồ thị G3 không có chu trình Euler nhưng nó có đường đi Euler a - c - d - e - b - ca - b, vì thế G3 là đồ thị nửa Euler.
Đồ thị G2 không có chu trình Euler cũng như đường đi Euler.
1.3.2. Đồ thị Hamilton
1.3.2.1. Chu trình Hamilton
Định nghĩa: Giả sử G = <X, U> là đồ thị vô hướng.
Chu trình Hamilton là chu trình đi qua tất cả các đỉnh của đồ thị, mỗi đỉnh đúng
một lần .
1.3.2.2. Đường Hamilton
Định nghĩa: Đường Hamilton trong đồ thị G = <X, U> là đường đi qua tất cả các
đỉnh mỗi đỉnh đúng một lần.

Hình 1.6. Đường đi Hamilton
1.4. Biểu diễn đồ thị trên máy tính
Để lưu trữ đồ thị và thực hiện các thuật toán khác nhau với đồ thị trên máy tính
cần phải tìm những cấu trúc dữ liệu thích hợp để mô tả đồ thị. Việc chọn cấu trúc dữ
liệu nào để biểu diễn đồ thị có tác động rất lớn đến hiệu quả của thuật toán.
Vì vậy, việc chọn lựa cấu trúc dữ liệu để biểu diễn đồ thị phụ thuộc vào từng tình
huống cụ thể (bài toán và thuật toán cụ thể). Trong mục này chúng ta sẽ xét một số
phương pháp cơ bản được sử dụng để biểu diễn đồ thị trên máy tính, đồng thời cũng
phân tích một cách ngắn gọn những ưu điểm cũng như những nhược điểm của chúng
1.4.1. Biểu diễn bằng ma trận kề, ma trận trọng số:
1.4.1.1. Ma trận kề
Xét đơn đồ thị vô hướng G=(V,E), với tập đỉnh V={ 1, 2,. . . ,n} , tập cạnh E={ e1,
e2,. . .,em }. Ta gọi ma trận kề của đồ thị G là ma trận.
A={ ai,j : i,j=1, 2,. . . ,n}
Với các phần tử được xác định theo qui tắc sau đây:
ai, j = 0, nếu (i,j) ∈ E và
ai,j = 1 , nếu (i,j) ∈ E, i, j=1, 2,. . .,n.
Ví dụ: Ma trận trận kề của đồ thị vô hướng cho trong hình 1 là:


8


Hình 1.7.Đồ thị vô hướng G và Đồ thị có hướng G1
1.4.1.2. Ma trận trọng số
Trong rất nhiều vấn đề ứng dụng của lý thuyết đồ thị, mỗi cạnh e=(u,v) của đồ
thị được gán với một con số c(e) (còn viết là c(u,v) gọi là trọng số của cạnh e. Đồ thị
trong trường hợp như vậy được gọi là đồ thị có trọng số. Trong trường hợp đồ thị có
trọng số, thay vì mà trận kề, để biểu diễn đồ thị ta sử dụng ma trận trọng số.
C= {c[i,j], i,j=1, 2,. . .,n}
với
c[i,j]=c(i,j) nếu (i,j) ∈ E
và c[i,j]= θ nếu (i,j) ∈ E
Ưu điểm lớn nhất của phương pháp biểu diễn đồ thị bằng ma trận kề (hoặc ma trận
trọng số) là để trả lời câu hỏi: Hai đỉnh u,v có kề nhau trên đồ thị hay không, chúng ta
chỉ phải thực hiện một phép so sánh. nhược điểm lớn nhất của phương pháp này là:
không phụ thuộc vào số cạnh của đồ thị, ta luôn phải sử dụng n2 đơn vị bộ nhớ để lưu
trữ ma trận kề của nó.
1.4.2. Danh sách cạnh
Trong trường hợp đồ thị thưa (đồ thị có số cạnh m thoả mãn bất dẳng thức: m<6n)
người ta thường dùng cách biểu diễn đồ thị dưới dạng danh sách cạnh.
Trong cách biểu diễn đồ thị bởi danh sách cạnh (cung) chúng ta sẽ lưu trữ danh
sách tất cả các cạnh (cung) của đồ thị vô hướng (có hướng). Một cạnh (cung) e=(x,y)
của đồ thị sẽ tương ứng với hai biến Dau[e], Cuoi[e]. như vậy, để lưu trữ đồ thị ta cần
sử dụng 2m đơn vị bộ nhớù. Nhược điểm của cách biểu diễn này là để xác định những
đỉnh nào của đồ thị là kề với một đỉnh cho trước chúng ta phải làm cỡ m phép so sánh
(khi duyệt qua danh sách tất cả các cạnh của đồ thị).
Chú ý: Trong trường hợp đồ thị có trọng số ta cần thêm m đơn vị bộ nhớ để lưu trữ
trọng số của các cạnh.
1.4.3. Danh sách kề

Trong rất nhiều vấn đề ứng dụng của lý thuyết đồ thị, cách biểu diễn đồ thị dưới
dạng danh sách kề là cách biểu diễn thích hợp nhất được sử dụng.

9


Trong cách biểu diễn này, với mỗi đỉnh v của đồ thị chúng ta lưu trữ danh sách các
đỉnh kề với nó, mà ta sẽ ký hiệu là: Ke(v)=  u∈ V: (v,u)∈ E
Khi đó vòng lặp thực hiện với mỗi một phần tử trong danh sách này theo thứ tự
các phần tử được sắp xếp trong nó sẽ được viết như sau: for u∈ Ke(v) do. . .
Chẳng hạn, trên PASCAL có thể mô tả danh sách này như sau (Gọi là cấu
trúc Forward Star):
Const
m=1000;  m-so canh 
n= 100;  n-so dinh 
var
Ke:array[1..m] of integer;
Tro:array[1..n+1] of integer;
Trong đó Tro[i] ghi nhận vị trí bắt đầu của danh sách kề của đỉnh i, i=1, 2,…,n,
Tro[n+1]=2m+1.
Khi đó dòng lệnh qui ước
for u ∈ Ke(v) do
begin
....
end.
Có thể thay thế bởi cấu trúc lệnh cụ thể trên PASCAL như sau:
For i:=Tro[v] to Tro[v+1]-1 do
Begin
U:=Ke[i];
....

End;
Trong rất nhiều thuật toán làm việc với đồ thị chúng ta thường xuyên phải thực
hiện các thao tác: Thêm hoặc bớt một số cạnh. Trong trường hợp này cấu trúc dữ liệu
dùng ở trên là không thuận tiện. Khi đó nên chuyển sang sử dụng danh sách kề liên kết
(Linked Adjancency List) như mô tả trong chương trình nhập danh sách kề của đồ thị
từ bàn phím và đưa danh sách đó ra màn hình sau đây:
Program AdjList;
Const
maxV=100;
Type
Link=^node;
node=record
v:integer;
next:link;
End;
Var
10


j,x,y,m,n,u,v:integer;
t:link;
Ke:array[1. .Vmax] of link;
Begin
Write(‘Cho so canh va dinh cua do thi:’); readln(m,n);
(*Khoi tao*)
for j:=1 to n do Ke[j]:=nil;
for j:=1 to m do
begin
write(‘Cho dinh dau va cuoi cua canh ‘,j,’:’);
readln(x,y);

new(t); t^.v:=x, t^.next:=Ke[y]; Ke[y]:=t;
new(t); t^.v:=y, t^.next:=Ke[x]; Ke[x]:=t;
end;
writeln(‘Danh sach ke cua cac dinh cua do thi:’);
for J:=1 to m do
begin
writeln(‘Danh sachcac dinh ke cua dinh ‘,j,’:’);
t:=Ke[j];
while t^.next<>nil do
begin
write(t^.v:4);
t:=t^.next;
end;
end;
readln;
End.
Kết luận: Lý thuyết đồ thị là mảng rất lớn nằm trong toán rời rạc, đồ thị đóng vai
trò quan trọng làm cơ sở toán cho tin học và có nhiều ứng dụng trong thực tiễn. Vì vậy
việc nghiên cứu cơ sở lý thuyết đồ thị là rất cần thiết giúp cho việc ứng dụng xây dựng
các thuật toán của đồ thị. Trong phạm vi nghiên cứu đề tài, những vấn đề mà tôi nêu
trên là một phần của lý thuyết đồ thị, nhằm mục đích phục vụ cho quá trình nghiên cứu
các chương sau.

11


CHƯƠNG 2: GIỚI THIỆU MỘT SỐ THUẬT TOÁN TRÊN ĐỒ THỊ
2.1. Thuật toán tìm kiếm trên đồ thị
2.1.1. Tìm kiếm theo chiều sâu trên đồ thị
Tìm kiếm ưu tiên chiều sâu hay tìm kiếm theo chiều sâu (DFS) là một thuật

toán duyệt hoặc tìm kiếm trên một cây hoặc một đồ thị. Thuật toán khởi đầu tại gốc
(hoặc chọn một đỉnh nào đó coi như gốc) và phát triển xa nhất có thể theo mỗi nhánh.
Ý tưởng thuật toán:
1. DFS trên đồ thị vô hướng cũng giống như khám phá mê cung với một cuộn
chỉ và một thùng sơn đỏ để đánh dấu, tránh bị lạc. Trong đó mỗi đỉnh s trong đồ thị
tượng trưng cho một cửa trong mê cung.
2. Ta bắt đầu từ đỉnh s, buộc đầu cuộn chỉ vào s và đánh đấu đỉnh này "đã thăm".
Sau đó ta đánh dấu s là đỉnh hiện hành u.
3. Bây giờ, nếu ta đi theo cạnh (u,v) bất kỳ.
4. Nếu cạnh (u,v) dẫn chúng ta đến đỉnh "đã thăm" v, ta quay trở về u.
5. Nếu đỉnh v là đỉnh mới, ta di chuyển đến v và lăn cuộn chỉ theo. Đánh
dấu v là "đã thăm". Đặt v thành đỉnh hiện hành và lặp lại các bước.
6. Cuối cùng, ta có thể đi đến một đỉnh mà tại đó tất cả các cạnh kề với nó đều
dẫn chúng ta đến các đỉnh "đã thăm". Khi đó, ta sẽ quay lui bằng cách cuộn ngược
cuộn chỉ và quay lại cho đến khi trở lại một đỉnh kề với một cạnh còn chưa được khám
phá. Lại tiếp tục quy trình khám phá như trên.
7. Khi chúng ta trở về s và không còn cạnh nào kề với nó chưa bị khám phá là
lúc DFS dừng.
Thuật toán:
Procedure DFS(v);
(* Tìm kiếm theo chiều sâu bắt đầu từ đỉnh v; Các biến Chuaxet,
Ke, là toàn cục*)
Begin
Tham_dinh(v);
Chuaxet[v] := false;
for u Ke(v) do
if Chuaxet[u] then DFS(u);
end; (* đỉnh v là đã duyệt xong *)
Khi đó, tìm kiếm theo chiều sâu trên đồ thị được thực hiện nhờ thuật toán sau:
BEGIN (* Initialiation *)

for v V do Chuaxet[u] := true;
for v V do
if Chuaxet[v] then DFS(v);
END.
2.1.2. Tìm kiếm theo chiều rộng trên đồ thị
12


Trong lý thuyết đồ thị, tìm kiếm theo chiều rộng (BFS) là một thuật toán tìm
kiếm trong đồ thị trong đó việc tìm kiếm chỉ bao gồm 2 thao tác: (a) cho trước một
đỉnh của đồ thị; (b) thêm các đỉnh kề với đỉnh vừa cho vào danh sách có thể hướng tới
tiếp theo.
Ý tưởng:
Thuật toán sử dụng một cấu trúc dữ liệu hàng đợi để lưu trữ thông tin trung gian
thu được trong quá trình tìm kiếm:
1.
Chèn đỉnh gốc vào hàng đợi (đang hướng tới)
2. Lấy ra đỉnh đầu tiên trong hàng đợi và quan sát nó

Nếu đỉnh này chính là đỉnh đích, dừng quá trình tìm kiếm và trả về kết quả.

Nếu không phải thì chèn tất cả các đỉnh kề với đỉnh vừa thăm nhưng chưa
được quan sát trước đó vào hàng đợi.
3. Nếu hàng đợi là rỗng, thì tất cả các đỉnh có thể đến được đều đã được quan sát
– dừng việc tìm kiếm và trả về "không thấy".
4. Nếu hàng đợi không rỗng thì quay về bước 2.
Thuật toán:
Procedure BFS(v);
(* Tìm kiếm theo chiều rộng bắt đầu từ đỉnh v; Các biến
Chuaxet, Ke là biến toàn cục *)

begin
QUEUE:= ;
QUEUE:<= v; (* Kết nạp v vào QUEUE *)
Chuaxet[v]:= false;
While QUEUE do
Begin
p <= QUEUE; (* Lấy p từ QUEUE *)
Thăm_đỉnh(p);
for u Ke(v) do
if Chuaxet[u] then
begin
QUEUE <= u; Chuaxet[u]:= false;
end;
end;
end;
Khi đó, tìm kiếm theo chiều rộng trên đồ thị thực hiện nhờ thuật toán sau:
BEGIN (* Initialization *)
for v V do Chuaxet[v]:= true;
for v V do
if Chuaxet[v] then BFS(v);
13


END.
2.1.3. Tìm đường đi và kiểm tra tính liên thông
Bài toán tìm đường đi giữa hai đỉnh
Giả sử s và t là hai đỉnh nào đó của đồ thị, tìm đường đi từ s đến t. Như trên đã
phân tích, thủ tục DFS(s) (BFS(s)) sẽ cho phép thăm tất cả các đỉnh thuộc cùng một
thành phần liên thông với s. Sau khi thực hiện xong thủ tục, nếu Chuaxet[t] = true,
điều đó có nghĩa là không có đường đi từ s đến t, còn nếu Chuaxet[t]=false thì t thuộc

cùng thành phần liên thông với s, hay nói một cách khác tồn tạiđường đi từ s đến t.
Trong trường hợp tồn tại đường đi, ta dùng biến Truoc[v] để ghi nhận đỉnh trước đỉnh
v trong đường đi tìm kiếm từ s đến v. Khi đó, đối với thủ tục DFS(v) cần sửa câu lệnh
if như sau:
if Chuaxet[u] then
begin
Truoc[u]:=v; DFS(u);
end;
Còn đối với thủ tục BFS(v) cần sửa đổi câu lệnh câu lệnh if trong nó như sau:
if Chuaxet[u] then
begin
QUEUE u; Chuaxet[u]:= false; Truoc[u]:= p;
end;
Đường đi cần tìm sẽ được khôi phục theo quy tắc sau:
T p1:= Truoc[t] p2:= Truoc[p1] … s.
2.2. Đồ thị có trọng số và bài toán tìm đường đi ngắn nhất
2.2.1. Đường đi ngắn nhất trong đồ thị không có trọng số.
Đồ thị không có trọng số là đồ thị hữu hạn trên các cạnh không có trọng số. Bài
toán tìm đường đi ngắn nhất giữa hai đỉnh a,b trong đồ thị không có trọng số G= U> là tìm đường đi giữa hai đỉnh a, b sao cho có số các cạnh là ít nhất [4].
Thuật toán
Bước 1: Tại đỉnh a ta ghi số 0; Các đỉnh có cạnh đi từ đỉnh a đến ta ghi số 1.
Giả sử ta đã ghi tới i, tức là ta đã đánh số được các tập đỉnh là A(0) = {a}, A(1),
A(2), ... , A(i) trong đó A(i) là tập tất cả các đỉnh được ghi bởi số i. Ta xác định tập
đỉnh được đánh số bởi số i + 1 là A(i+1) = {x / x X, x A(k) với k = 0,...,i và tồn tại y
A(i) sao cho từ y có cạnh (cung) tới x}. Do tính hữu hạn của đồ thị, sau một số hữu
hạn các bước, thuật toán dừng lại và cho kết quả là tập các đỉnh có chứa b được đánh
số bởi m là A(m).
Bước 2: Do bước 1 thì đỉnh b được đánh số bởi m, điều này chứng tỏ đường đi từ
a đến b có m cạnh (cung) và là đường ngắn nhất đi từ a đến b. Để tìm tất cả các đường

có độ dài m ngắn nhất đi từ a đến b, ta xuất phát từ b đi ngược về a theo đúng nguyên
tắc:
14


- Tìm tất cả các đỉnh có cạnh tới b được ghi số m-1, giả sử đó là x[i,k] (k=1,2,..)
- Với mỗi đỉnh x[i,k] tìm tất cả các đỉnh có cạnh với x[i,k] (k=1,2...) ghi số m-2.
Bằng cách lùi dần trở lại, đến một lúc nào đó gặp đỉnh ghi số 0, đó chính là đỉnh a.
Tất cả các đường xác định theo các bước trên là đường đi từ a đến b có độ dài ngắn
nhất là m cần tìm.
2.2.2. Tìm đường đi ngắn nhất
Trong phần này chúng ta chỉ xét đồ thị có hướng G=(V,E), |V| = n, |E|=m với các
cung được gán trọng số, nghĩa là, mỗi cung (u,v) E của nó được đặt tương ứng với một
số thực a(u,v) gọi là trọng số của nó.
2.2.3. Thuật toán Ford – Bellman
Procedure Ford_Bellman;
Đầu vào: Đồ thị có hướng G=(V,E) với n đỉnh, s ∈ V là đỉnh xuất phát, a[u,v], u,
v ∈ V, ma trận trọng số;
Giả thiết: Đồ thị không có chu trình âm.
Đầu ra: Khoảng cách từ đỉnh s đến tất cả các đỉnh còn lại d[v],
Truoc[v], ghi nhận đỉnh đi trước v trong đường đi ngắn nhất từ s đến v.
Begin (* Khởi tạo *)
for v V do
begin
d[v]:= a[s,v];
Truoc[v]:= s;
end;
d[s]:= 0;
for k:= 1 to n-2 do
for v V \ {s} do

for u V do
if d[v] > d[u] + a[u,v] then
begin
d[v]:= d[u] + a[u,v];
Truoc[v]:= u;
end;
end;
2.2.4. Thuật toán Dijkstra
Bài toán: Cho một đồ thị có hướng G=(V,E), một hàm trọng số w: E → [0, ∞) và
một đỉnh nguồn s. Cần tính toán được đường đi ngắn nhất từ đỉnh nguồn s đến mỗi
đỉnh của đồ thị.
Ví dụ: Chúng ta dùng các đỉnh của đồ thị để mô hình các thành phố và các cạnh
để mô hình các đường nối giữa chúng. Khi đó trọng số các cạnh có thể xem như độ dài
của các con đường (và do đó là không âm). Chúng ta cần vận chuyển từ thành phố A
15


đến thành phố B. Thuật toán Dijkstra sẽ giúp chỉ ra đường đi ngắn nhất chúng ta có thể
đi.
Trọng số không âm của các cạnh của đồ thị mang tính tổng quát hơn khoảng cách
hình học giữa hai đỉnh đầu mút của chúng. Ví dụ, với 3 đỉnh X, Y, Z đường đi X-Y-Z
có thể ngắn hơn so với đường đi trực tiếp X-Y.
Ý tưởng: Ý tưởng của thuật toán được chứng minh như sau.
Chúng ta sẽ chỉ ra, khi một đỉnh v được bổ sung vào tập S, thì d[v] là giá trị
của đường đi ngắn nhất từ nguồn s đến v.
Theo định nghĩa nhãn d, d[v] là giá trị của đường đi ngắn nhất trong các đường đi
từ nguồn s, qua các đỉnh trong S, rồi theo một cạnh nối trực tiếp u-v đến v.
Giả sử tồn tại một đường đi từ s đến v có giá trị bé hơn d[v]. Như vậy trong đường
đi, tồn tại đỉnh giữa s và v không thuộc S. Chọn w là đỉnh đầu tiên như vậy.
Thuật toán:

procedure Dijkstra;
(* Đầu vào:đồ thị có hướng G=(V,E) với n đỉnh.
s ∈ V là đỉnh xuất phát, a[u,v],u.v ∈ V, ma trận trọng số;
Giả thiết : a[u,v] 0, u,v V .
Đầu ra: Khoảng cách từ đỉnh s đến tất cả các đỉnh còn lại d[v,
v ∈V. Truoc[v],v ∈ V ghi nhận đỉnh đi trước v trong đường đi ngắn nhất từ s đến v*)
Begin (*khởi tạo*)
for v V do
begin
d[v]:= a[s,v];
truoc[v]= s;
end;
d[s]:= 0; T:= V\{s}; (* T là tập đỉnh có nhãn tạm thời *)
while T do
begin
Tìm đỉnh u T thoả mãn d[u] = min{d[z]:z T};
T:= T\{u}; (* Cố định nhãn của đỉnh u *)
for v T do (* Gán lại nhãn cho các đỉnh trong T *)
if d[v] > d[u] + a[u,v] then
begin
d[v]:= d[u] + a[u,v];
Truoc[v]:= u;
end;
end;
end;
2.2.5. Thuật toán Floyd – đường đi ngắn nhất giữa tất cả các cặp đỉnh.
16


Cho G=(V,E) là một đồ thị có hướng, có trọng số. Để tìm đường đi ngắn nhất giữa

mọi cặp đỉnh của G, ta áp dụng thuật toán Dijkstra nhiều lần hoặc áp dụng thuật toán
Floyd được trình bày dưới đây.
Giả sử V={v1, v2, ..., vn} và có ma trận trọng số là W W0. Thuật toán Floyd xây
dựng dãy các ma trận vuông cấp n là Wk ( 0 ≤ 𝑘 ≤ 𝑛 )
Procedure Floyd
Procedure Floyd;
(* Tìm đường đi ngắn nhất giữa tất cả các cặp đỉnh
Đầu vào: Đồ thị cho bởi ma trận trọng số a[i,j], i, j =1, 2,. . ,n.
Đầu ra:
Ma trận đường đi ngắn nhất giữa các cặp đỉnh
d[i,j]=, i,j = 1, 2. . .,n,
trong đó d[i,j] cho độ dài đường đi ngắn nhất từ đỉnh i đến đỉnh j.
Ma trận ghi nhận đường đi
p[i,j], i, j = 1, 2.. . , n,
trong đó p[i,j] ghi nhận đỉnh đi trước đỉnh j trong đường đi ngắn nhất từ i đến j. *)
begin
(* Khởi tạo *)
for i:=1 to n do
for j:=1 to n do
begin
d[i,j]:=a[i.j];
p[i.j]:=i;
end;
(* Bước lặp *)
for k:=1 to n do
for i:=1 to n do
for j:=1 to n do
if d[i,j]>d[i,k]+d[k,j] then
begin
d[i,j]+d[i,k]+d[k,j];

p[i,j]>p[k,j];
end;
end;

17


CHƯƠNG 3: LUỒNG CỰC ĐẠI VÀ HƯỚNG ỨNG DỤNG
3.1. Giới thiệu luồng cực đại
3.1.1. Bài toán luồng trên mạng.
Nhiều bài toán quy hoạch tuyến tính có thể quy về bài toán làm cực tiểu phí tổn
vận chuyển hàng trong một mạng (gồm các nút và các cung đường) sao cho đảm bảo
được các nhu cầu ở một số nút khi đã biết nguồn cung cấp tại một số nút khác.
Các bài toán như vậy được gọi là các bài toán luồng trên mạng hoặc bài toán
tuyến tính. Lớp này bao gồm các bài toán quen thuộc trong thực tế như: bài toán
vận tải, các bài toán mạng điện và mạng giao thông, các bài toán quản lý và phân bổ
vật tư, bài toán bổ nhiệm, bài toán kế hoạch tài chính, bài toán đường ngắn nhất, bài
toán luồng cực đại …[6]
3.1.2. Bài toán luồng cực đại trong mạng
Bài toán luồng cực đại [1] trong mạng cũng là một trong số những bài toán tối ưu
trên đồ thị tìm được những ứng dụng rộng rãi trong thực tế cũng như những ứng dụng
thú vị trong lý thuyết tổ hợp. Bài toán được đề xuất vào đầu những năm 1950, và gắn
liền với tên tuổi của hai nhà bác học Mỹ là L.R.Fordvà D.R.Fulkerson. Bài toán luồng
cực đại trong mạng có nhiều ứng dụng trong thực tế như: Bài toán xác định cường độ
dòng lớn nhất của dòng vận tải giữa hai nút của một bản đồ giao thông, bài toán tìm
luồng dầu lớn nhất có thể bơm từ tàu chở dầu vào bể chứa của một hệ thống đường
ống dẫn dầu…Ngoài ra, ứng dụng của bài toán còn để giải các bài toán như: Bài toán
đám cưới vùng quê, bài toán về hệ thống đại diện chung, bài toán phân nhóm sinh
hoạt, bài toán lập lịch cho hội nghị…
3.1.3. Mạng, luồng trong mạng, bài toán luồng cực đại

Định nghĩa 1: Mạng (Network) [1]
Ta gọi mạng là đồ thị có hướng G=(V,E), trong đó có duy nhất một đỉnh s không
có cung đi vào gọi là điểm phát, duy nhất một đỉnh t không có cung đi ra gọi là điểm
thu và mỗi cung e = (v,w) ∈ E được gán với một số không âm c(e) = c(v,w) gọi là khả
năng thông qua của cung e.
Để thuận tiện cho việc trình bày ta sẽ quy ước rằng nếu không có cung (v,w) thì
khả năng thông qua c(v,w) được gán bằng 0.
Định nghĩa 2. Luồng (Luồng trong mạng)
Giả sử cho mạng G=(V,E). Ta gọi luồng f trong mạng G=(V,E) là ánh xạ f: E →
R+ gán cho mỗi cung e =(v,w) ∈ E một số thực không âm f(e) = f(v,w), gọi là luông
trên cung e, thoả mãn các điều kiện sau:
1. Luồng trên mỗi cung e ∈ E không vượt quá khả năng thông qua của nó: 0 ≤ f
(e) ≤ c(e),
2. Điều kiện cân bằng luồng trên mỗi đỉnh của mạng : Tổng luồng trên các cung
đi vào đỉnh v bằng tổng luồng trên các cung đi ra khỏi đỉnh v, nếu v ¹ s,t:

18


Div f (v) 

 f (v)   f (v, w)  0

w ( v )

w ( v )

Trong đó: tập các đỉnh của mạng mà từ đó có cung đến v, tập các đỉnh của mạng
mà từ v có cung đến nó:
  (v)  w V : (w, v)  E,   (v)  w V : (v, w)  E.


3.Giá trị của luồng f là số
val( f ) 

 f (s, w)   f (w, t ).

w ( s )

w (t )

Tính chất của luồng.
Với tập B ⊆ V, ký hiệu:
W-(B) = { (a, b)∈ E | a∉B,b∈B } - tập cạnh từngoài B đi vào B.
W+(B) = { (a, b)∈ E | a∈B,b∉B } - tập cạnh từ B đi ra khỏi B.
Khi đó nếu tập con các đỉnh B không chứa x0 và z thì: t (W-(B)) =t (W+(B)).Theo
tính chất b) của luồng: ∑ t (W-(x)) =∑ t (W+(x)).
Cạnh kề với đỉnh x nếu có đỉnh đầu và đỉnh cuối đều nằm trong tập B thì nó sẽ có
mặt ở cả hai vế củađẳng thức đúng một lần, do đó có thể giản ước.
3.2. Ứng dụng bài toán luồng cực đại trong mạng
Cho mạng G=(V,E). Hãy tìm luồng f* trong mạng với giá trị luồng val(f*) là lớn
nhất. Luồng như vậy ta sẽ gọi là luồng cực đại trong mạng.
Bài toán như vậy có thể xuất hiện trong rất nhiều ứng dụng thực tế . Chẳng hạn khi
cần xác định cường độ lớn nhất của dòng vận tải giữa 2 nút của một bản đồ giao thông.
Trong ví dụ này của bài toán luồng cực đại xẽ chỉ cho ta các đoạn đường đông xe nhất
và chúng tạo thành “chỗ hẹp” tương ứng với dòng giao thỗng xét theo hai nút được
chọn. Một ví dụ khác là nếu xét đồ thị tương ứng với một hệ thống dẫn dầu. Trong đó
các ống tương ứng với các cung, điểm phát có thể có thể là tàu chở dầu, điểm thu là bể
chứa, còn những điểm nối giữa các ống là các nút của đồ thị. Khả năng thông qua của
các cung tường ứng với tiết diện các ống.Cần phải tìn luộng dầu lớn nhất có thể bơm
từ dầu vào bể chứa.

Thuật toán Ford-Fulkerson [4]
Việc chứng minh định lý luồng cực đại-lát cắt cực tiểu ngay lập tức cho chúng ta
một thuật toán Tìm luồng cực đại- thuật toán Ford-Fulkerson:
- Khởi tạo một luồng bằng 0.
- Trong khi đồ thị tăng luồng của f còn có đường đi cơ bản (đường tăng luồng),
thì tìm một đường đi như thế, nâng luồng dọc theo đường đi này.
- Khi không còn đường đi cơ bản nữa thì f là luồng cực đại.
Đoạn chương trình sau minh họa thuật toán Ford-Fulekerson bằng NNLT
Pascal, trong đó sử dụng tìm kiếm sâu DFS để tìm một đường tăng luồng trên đồ thị
tăng luồng.

19


Procedure Findpath(u:integer);{tìm đường tăng luồng trên Gf }
Var
v:integer;
Front, rear: integer;
Begin
Trace [s]:= n+1;
{Trace[v] = 0 nghĩa là v chưa được thăm}
For v :=1 to n do
Begin
{xét v kề u chưa thăm trên Gf}
If (trace[v] = 0) and (c[u,v]>f[u,v]) then
Begin
Trace[v]:=u;
if v=t then {đến được t thì dừng thuật toán }
Begin
path := True;

exit;
End;
findpath(v);
End;
if path =true then exit;
End;
End;
Procedure IncFlow; {tăng luông dọc đường tăng luồng: f :=(f+ff)}
Var
Delta, u, v: Integer;
Begin
{Tính Delta = Δf}
Delta:= Maxint;
V :=t;
Repeat
U := trace[v];
If c[u,v] - f[u,v] < Delta then Delta := c[u,v] – f[u,v] ;
V := u;
Until v = s;
{f := (f+ff)
V :=t;
Repeat
u := trace[v];
f[u,v] :=f[u,v] + Delta ;
20


×