LỜI CAM ĐOANTôi xin cam đoan, kết quả luận văn với đề tài “Nghiên cứu thuật toán tìm kiếm trên đồ thị và ứng dụng trong bồi dưỡng học sinh giỏi” này là công trình nghiên cứu của riêng tô
Trang 2BỘ GIÁO DỤC VÀ ĐÀO TẠO TRƯỜNG ĐẠI HỌC VINH
LUẬN VĂN THẠC SĨ CÔNG NGHỆ THÔNG TIN
Người hướng dẫn khoa học: TS Phan Lê Na
NGHỆ AN, 7/2018
Trang 3LỜI CAM ĐOAN
Tôi xin cam đoan, kết quả luận văn với đề tài “Nghiên cứu thuật toán tìm kiếm trên đồ thị và ứng dụng trong bồi dưỡng học sinh giỏi” này là công trình nghiên cứu của riêng tôi, dưới sự hướng dẫn của TS Phan Lê Na Các số liệu sử dụng trong luận văn là trung thực, các tài liệu tham khảo được trích dẫn và chú thích đầy đủ Các kết quả nghiên cứu được trình bày trong luận văn chưa từng được công bố tại bất kỳ công trình nào khác
Tác giả Hoàng Thanh Hà
Trang 4LỜI CẢM ƠN
Trước hết, tôi muốn gửi lời cảm đến các thầy, cô trong khoa Công nghệ thông tin - Trường Đại học Vinh đã truyền đạt các kiến thức quý báu cho tôi trong suốt thời gian học tập tại trường Đặc biệt, tôi xin gửi lời cảm ơn sâu sắc tới cô giáo hướng dẫn TS Phan Lê Na, người đã tận tình chỉ bảo và hướng dẫn về mặt chuyên môn cho tôi trong suốt quá trình thực hiện luận văn này
Cũng qua đây, tôi xin gửi lời cảm ơn đến Ban Giám hiệu trường THPT
Cờ Đỏ, nơi tôi đang công tác đã tạo mọi điều kiện thuận lợi cho tôi trong thời gian học tập cũng như trong suốt quá trình thực hiện luận văn tốt nghiệp
Cuối cùng, tôi xin cảm ơn gia đình, bạn bè, đồng nghiệp đã luôn ủng
hộ, động viên tôi rất nhiều để tôi yên tâm nghiên cứu và hoàn thành luận văn
Trong suốt quá trình làm luận văn, bản thân tôi đã cố gắng tập trung tìm hiểu, nghiên cứu và tham khảo thêm nhiều tài liệu liên quan Tuy nhiên,
do thời gian hạn chế và bản thân còn chưa có nhiều kinh nghiệm trong nghiên cứu khoa học, chắc chắn bản luận văn vẫn còn nhiều thiếu sót Tôi rất mong được nhận sự chỉ bảo của các Thầy Cô giáo và các góp ý của bạn bè, đồng nghiệp để luận văn được hoàn thiện hơn
Nghệ An, tháng 7 năm 2018
Tác giả Hoàng Thanh Hà
Trang 5DANH MỤC CÁC TỪ VIẾT TẮT STT KÝ HIỆU VIẾT TẮT
Trang 6DANH MỤC CÁC HÌNH VẼ VÀ BẢNG
Trang
Hinh 1.1 Đồ thị vô hướng và Đồ thị có hướng 11
Hinh 1.2 Đơn đồ thị 12
Hinh 1.3 Đa đồ thị 13
Hinh 1.4 Đồ thị và trận lân cận biểu diễn 14
Hinh 1 5 Đồ thị vô hướng 15
Hinh 1.6 Đồ thị có hướng 15
Hinh 1.7 Đồ thị G 16
Hinh 1.8 Đồ thị G 17
Hinh 1.9 Mô phỏng bài toán 3.4 38
Hinh 1.10 Mô phỏng bài toán 3.8 54
Trang 7
MỤC LỤC
LỜI CAM ĐOAN 1
LỜI CẢM ƠN 2
DANH MỤC CÁC TỪ VIẾT TẮT 3
DANH MỤC CÁC HÌNH VẼ VÀ BẢNG 4
MỞ ĐẦU 8
1 Sự cần thiết của vấn đề nghiên cứu 8
2 Mục tiêu nghiên cứu 9
3 Đối tượng và phạm vi nghiên cứu 9
3.1 Đối tượng nghiên cứu 9
3.2 Phạm vi nghiên cứu 9
4 Nội dung nghiên cứu 9
5 Phương pháp nghiên cứu 9
5.1 Phương pháp nghiên cứu tài liệu 9
5.2 Phương pháp thực nghiệm 9
6 Đóng góp của luận văn 10
7 Cấu trúc luận văn 10
CHƯƠNG 1 MỘT SỐ KIẾN THỨC CƠ BẢN VỀ ĐỒ THỊ 11
1.1 Các khái niệm cơ bản về đồ thị 11
1.1.1 Khái niệm đồ thị (Graph) 11
1.1.2 Phân loại đồ thị 11
1.2 Biểu diễn đồ thị 13
1.2.1 Biểu diễn bằng ma trận lân cận 14
1.2.2 Biểu diễn bằng danh sách lân cận 15
1.3 Phép duyệt một đồ thị 15
1.3.1 Phát biểu bài toán 15
1.3.2 Các thuật toán tìm kiếm DFS và BFS 16
1.4 Độ phức tạp tính toán của thuật toán DFS và BFS 18
Trang 8CHƯƠNG 2: MỘT SỐ BÀI TOÁN TÌM KIẾM TRÊN ĐỒ THỊ 20
2.1 Bài toán tìm đường đi ngắn nhất trên đồ thị có trọng số 20
2.1.1 Phát biểu bài toán 20
2.1.2 Thuật toán Ford – Bellman 20
2.1.3 Thuật toán Dijkstra 22
2.1.4 Độ phức tạp 23
2.2 Các thuật toán tìm kiếm trên cây khung 24
2.2.1 Bài toán cây khung 24
2.2.2 Thuật toán Prim 24
2.2.3 Thuật toán Kruskal 25
2.2.4 Độ phức tạp 27
2.3 Bài toán tìm chu trình Hamilton qua tất cả các đỉnh của đồ thị 27
2.3.1 Phát biểu bài toán 27
2.3.2 Giới thiệu thuật toán tìm chu trình Hamilton 28
CHƯƠNG 3: ỨNG DỤNG THUẬT TOÁN TÌM KIẾM TRÊN 29
ĐỒ THỊ ĐỂ GIẢI MỘT SỐ BÀI TOÁN HSG 29
3.1 Ứng dụng giải bài toán 29
3.1.1 Bài toán đi xem world cup 2018 bằng xe buýt 29
3.1.2 Bài toán đường hầm 32
3.1.3 Đếm nhóm bạn trong Hội trại 34
3.1.4 Bài toán trọng tải 37
3.1.5 Trạm tiếp nước ngọt 40
3.1.6 Đường đi đến số 0 43
3.1.7 Bài toán Tour du lịch rẻ tiền 46
3.1.8 Bài toán xây cầu vượt biển 53
3.1.9 Bài toán xây dựng đường 55
3.1.10 Bài toán thỏ và cà rốt 61
3.2 Thiết kế tổ chức dạy học 68
Trang 9KẾT LUẬN 71
1 Kết quả đạt được 71
2 Đóng góp của đề tài 71
3 Phạm vi áp dụng 71
4 Hướng phát triển 71
TÀI LIỆU THAM KHẢO 72
Trang 10MỞ ĐẦU
1 Sự cần thiết của vấn đề nghiên cứu
Một trong những tiêu chí để đánh giá chất lượng một trường trung học phổ thông (THPT) đó là số lượng học sinh giỏi của trường so với mặt bằng chung của tỉnh, của cả nước Môn Tin học được đưa vào giảng dạy chính thức
ở trường THPT từ năm học 2006 - 2007 tuy nhiên trong thực tế môn Tin học
đã được đưa vào tham gia thi học sinh giỏi cấp tỉnh, cấp quốc gia từ rất lâu
Chúng ta biết rằng để có kết quả cao trong kỳ thi tuyển chọn học sinh giỏi môn Tin học nói chung thì học sinh phải có vốn kiến thức về thuật toán
để giải được các bài toán khó (đặc biệt là các thuật toán nâng cao), sau đó học sinh sẽ sử dụng ngôn ngữ lập trình nào đó để lập trình dựa vào thuật toán đã tìm được và giải bài toán theo yêu cầu Chương trình giảng dạy ở sách giáo khoa của môn Tin học hiện hành trong trường THPT có lượng kiến thức rất hạn chế và vô cùng đơn giản, không đủ cơ sở và không thể là nền tảng để học sinh có thể dựa vào vốn kiến thức đó tham gia một kỳ thi học sinh giỏi cấp tỉnh hay cao hơn Câu hỏi đặt ra: “Làm thế nào để học sinh có thể đạt kết quả cao trong các kỳ thi học sinh giỏi môn Tin học trong trường THPT?” yêu cầu các giáo viên giảng dạy môn Tin học trong trường THPT phải suy nghĩ giải quyết
Lý thuyết đồ thị là một phương pháp rất hiệu quả để giải nhiều bài toán tin học, trong các kì thi học sinh giỏi Tin học THPT và các kì thi Olympic Tin học học sinh, bài toán về lý thuyết đồ thị là một trong những nội dung được quan tâm nhiều
Vì những lý do trên tôi xin chọn đề tài “Nghiên cứu thuật toán tìm kiếm trên đồ thị và ứng dụng trong bồi dưỡng học sinh giỏi”
Trang 112 Mục tiêu nghiên cứu
- Tạo ra nguồn tài liệu tham khảo cho giáo viên giảng dạy bộ môn Tin học về phương pháp lý thuyết đồ thị
- Góp phần nâng cao kết quả trong các kỳ học sinh giỏi tỉnh và giáo viên
3 Đối tượng và phạm vi nghiên cứu
3.1 Đối tượng nghiên cứu
4 Nội dung nghiên cứu
- Nghiên cứu lý thuyết về phương pháp duyệt đồ thị
- Ứng dụng phương pháp duyệt đồ thị để giải một số nhóm bài toán bồi dưỡng học sinh giỏi
5 Phương pháp nghiên cứu
5.1 Phương pháp nghiên cứu tài liệu
- Thu thập, phân tích các tài liệu và thông tin liên quan đến thuật toán tìm kiếm
- Lựa chọn một số bài toán giải bằng phương pháp duyệt đồ thị
5.2 Phương pháp thực nghiệm
- Sử dụng phương pháp duyệt đồ thị trong bồi dưỡng HSG tỉnh bậc THPT
Trang 12- Dùng ngôn ngữ lập trình Pascal để cài đặt một số bài toán, chạy thử nghiệm trên một số bộ dữ liệu để đánh giá kết quả
- Thiết kế tổ chức dạy học chuyên đề duyệt đồ thị trong chuyên đề bồi dưỡng HSG tỉnh
6 Đóng góp của luận văn
- Đề tài cung cấp một số bài toán giải bằng phương pháp duyệt đồ thị
và áp dụng rất tốt trong công tác bồi dưỡng HSG
- Đề tài làm tài liệu tham khảo thiết thực cho giáo viên dạy THPT và học sinh
- Thiết kế tổ chức dạy học chuyên đề duyệt đồ thị trong chuyên đề bồi dưỡng HSG tỉnh
7 Cấu trúc luận văn
Ngoài phần mở đầu và phần kết luận, nội dung luận văn được chia thành 3 chương:
Chương 1 Một số khái niệm cơ bản trên đồ thị
Nội dung chương giới thiệu những khái niệm cơ bản về đồ thị, các thuật toán tìm kiếm trên đồ thị và đánh giá độ phức tạp một số thuật toán
Chương 2: Một số bài toán tìm kiếm trên đồ thị
Chương này giới thiệu một số bài toán tìm kiếm trên đồ thị
Chương 3 Ứng dụng thuật toán tìm kiếm trên đồ thị giải một số bài toán HSG Chương này chúng tôi sử dụng NNLT Pascal để cài đặt một số bài toán sử dụng trong kì thi học sinh giỏi và thiết kế tổ chức dạy học chuyên đề thuật toán tìm kiếm trên đồ thị
Trang 13CHƯƠNG 1 MỘT SỐ KIẾN THỨC CƠ BẢN VỀ ĐỒ THỊ
1.1 Các khái niệm cơ bản về đồ thị
1.1.1 Khái niệm đồ thị (Graph)
Là một cấu trúc rời rạc gồm các đỉnh và các cạnh nối các đỉnh đó
Được mô tả hình thức: G=(V, E) Trong đó:
V gọi là tập các đỉnh (Vertices) hay các nút và một tập hữu hạn E gọi là tập các cạnh hay là các cung (Edges) Có thể coi E là tập các cặp (v1, v2) với v1 và v2 là hai đỉnh của V [2]
Đa đồ thị: G được gọi là đa đồ thị nếu giữa hai đỉnh v 1, v2 của V có thể
có nhiều hơn 1 cạnh trong E nối từ v1 tới v2
Đồ thị vô hướng
Đồ thị có hướng
Trang 14Đồ thị vô hướng: G được gọi là đồ thị vô hướng nếu các cạnh trong E
là không định hướng, tức là cạnh nối hai đỉnh v1, v2 bất kỳ cũng là cạnh nối hai đỉnh v2, v1
Đồ thị có hướng: G được gọi là đồ thị có hướng nếu các cạnh trong E
là có định hướng, có thể có cạnh nối từ đỉnh v1 tới đỉnh v2 nhưng chưa chắc đã
có cạnh nối từ đỉnh v2 tới đỉnh v1 Hay nói cách khác, nếu (v1,v2) khác với cung (v2,v1) thì ta có một đồ thị định hướng Lúc đó (v1,v2) được gọi là cung định hướng từ v1,v2 Trong đồ thị có hướng, các cạnh được gọi là các cung
Đồ thị vô hướng cũng có thể coi là đồ thị có hướng nếu như ta coi cạnh
nối hai đỉnh v1, v2 bất kỳ tương đương với hai cung (v1, v2) và (v2, v1) Tức là
thứ tự các nút trên cung không được coi trọng thì ta có đồ thị không định
hướng (vô hướng) [1]
Ví dụ 1.1: Đơn đồ thị, đa đồ thị
Vô hướng Có hướng
(Đồ thị không định hướng) (Đồ thị định hướng)
Hinh 1.2 Đơn đồ thị
Trang 15Vô hướng
(Đồ thị không định hướng)
Có hướng (Đồ thị định hướng)
Một đường đi đơn là đường đi mà mọi đỉnh trên đó, trừ đỉnh đầu và
đỉnh cuối, đều khác nhau
Một chu trình là một đường đi đơn mà đỉnh đầu và đỉnh cuối trùng
nhau
Trong đồ thị G hai đỉnh vi, và vj gọi là liên thông nếu có một đường đi
từ đỉnh vi tới vj
Một đồ thị G được gọi là liên thông nếu đối với mọi cặp đỉnh phân biệt
vi, vj trong V(G) đều có một đường đi từ vi tới vj
1.2 Biểu diễn đồ thị
Có nhiều cấu trúc được sử dụng để biểu diễn đồ thị Việc chọn lựa cấu trúc nào là tùy thuộc vào các ứng dụng và các phép xử lý cần tác động lên đồ thị trong ứng dụng ấy Ta xét hai cách biểu diện sau:
Trang 161.2.1 Biểu diễn bằng ma trận lân cận
Xét đồ thị G(V,E) với V gồm có n đỉnh (n≥1) mà giả sử các đỉnh đã được đánh số thứ tự theo một quy định nào đó Ma trận lân cận A biểu diễn G
là một ma trận vuông kích thước n x n Các phần tử của ma trận có giá trị 0 hoặc 1 Nếu phần tử Aij =1 thì điều đó có nghĩa tồn tại một cung (vi ,vj) trong
E, còn nếu Aij= 0 thì không tồn tại cung như vậy
Hinh 1.4 Đồ thị và trận lân cận biểu diễn
Rõ ràng đồ thị không định hướng thì ma trận lân cận biểu diễn nó có
các phần tử đối xứng qua đường chéo chính, nghĩa là có phần tử bằng 1 ở hàng i cột j thì cũng có phần tử bằng 1 ở hàng j cột i Còn đồ thị định hướng
không phải như vậy
Trang 171.2.2 Biểu diễn bằng danh sách lân cận
Trong cách biểu diễn này, n hàng của ma trận lân cận được thay đổi bởi
n danh sách móc nối
Với mỗi đỉnh của G có một danh sách tương ứng Các nút trong danh
sách i biểu diễn các đỉnh lân cận của nút i Mỗi nút có hai trường VERTER
và LINK
Trường VERTER chứa chỉ số (số thứ tự) của các đỉnh lân cận của đỉnh i Trường LINK chứa con trỏ, trỏ tới nút tiếp theo trong danh sách
1.3 Phép duyệt một đồ thị
1.3.1 Phát biểu bài toán
Cho đồ thị G = (V, E) và s và t là hai đỉnh của đồ thị Yêu cầu: Hãy chỉ
ra một đường đi từ s đến t (nếu có)
Ví dụ 1.3: Đồ thị vô hướng và đồ thị có hướng
Hinh 1 5 Đồ thị vô hướng Hinh 1.6 Đồ thị có hướng
Trên cả hai đồ thị, (1, 2, 3, 4) là đường đi đơn độ dài 3 từ đỉnh 1 tới đỉnh 4 Bởi (1, 2) (2, 3) và (3, 4) đều là các cạnh (hay cung)
Làm sao để duyệt tất cả các đỉnh có thể đến được từ một đỉnh xuất phát nào đó? Vấn đề này đưa về một bài toán liệt kê mà yêu cầu của nó là không được bỏ sót hay lặp lại bất kỳ đỉnh nào Vì vậy, cần phải xây dựng những thuật toán cho phép duyệt một cách hệ thống các đỉnh, những thuật toán như vậy gọi là những thuật toán tìm kiếm trên đồ thị Trong lý thuyết đồ thị, người
Trang 18ta quan tâm đến hai thuật toán cơ bản nhất: Thuật toán tìm kiếm theo chiều sâu (depth Firrst search-DFS) và thuật toán tìm kiếm theo chiều rộng (Breadth first search-BFS), ngoài ra còn có các thuật toán LDFS, IDA…
1.3.2 Các thuật toán tìm kiếm DFS và BFS
a Thuật toán tìm kiếm theo chiều sâu (DFS)
Tư tưởng của thuật toán có thể trình bày như sau: Đỉnh xuất phát v
được thăm Tiếp theo đó, một đỉnh w chưa được thăm, mà là lân cận của v, sẽ được chọn và phép tìm kiếm theo chiều sâu xuất phát từ đỉnh w lại được thực
hiện Ý tưởng đó gợi ý cho ta viết một thủ tục đệ quy DFS(w) mô tả việc
duyệt từ đỉnh s bằng cách thông báo thăm đỉnh s và tiếp tục quá trình duyệt
DFS(w) với w là một đỉnh chưa thăm kề với s Để quá trình duyệt không lặp
lại bất kì đỉnh nào, ta dùng kỹ thuật đánh dấu, khi thăm một đỉnh, ta sẽ đánh dấu đỉnh đó lại để các bước duyệt đệ quy kế tiếp không thăm lại đỉnh đó nữa
Ví dụ 1.4: Hãy cho biết kết quả phép duyệt và giải thuật theo chiều sâu
đồ thị Hình 1.7
Nếu đồ thị G có dạng như sau và đỉnh xuất phát là đỉnh v1 thì dãy các đỉnh được thăm sẽ là:
Hinh 1.7 Đồ thị G
Trang 19Ban đầu thăm V1, rồi tới V2 (tất nhiên có thể V3 ) rồi V4, V8 , V5; do V5 không có lân cận nào chưa được thăm nên phải quay lại V8 để thăm tiếp V6 rồi V3, V7
Giải thuật duyệt theo chiều sâu như sau:
Pocedure DFS (v);
<1 VISITER (v):=1; {ở đây VISITER dùng đề đánh dấu các đỉnh đã được thăm}
<2 For mối đỉnh w lân cận của v do
If VISITER (w)=0 then Call DFS(w);
<3.return [2]
b Thuật toán tìm kiếm theo chiều rộng (DBS)
Ý tưởng của giải thuật như sau:
Đỉnh xuất phát v ở đây cũng được thăm đầu tiên, nhưng có khác với DFS ở chỗ là: sau đó các đỉnh chưa được thăm mà là lân cận của v sẽ được
thăm kế tiếp theo nhau, rồi mới đến các đỉnh chưa được thăm là lân cận lần lượt của các đỉnh này và cứ tương tự như vậy
Ví dụ 1.5: Hãy cho biết kết quả phép duyệt và giải thuật theo chiều
rộng đồ thị Hình 1.8
Nếu đồ thị G có dạng như sau và đỉnh xuất phát là đỉnh v1 thì dãy các
đỉnh được thăm sẽ là:
Hinh 1.8 Đồ thị G
Trang 20Thăm V1 rồi đến V2, V3… Tiếp theo là V4, V5, V6, V7 , cuối cùng V8
Giải thuật duyệt theo chiều rộng như sau:
Procedure BFS(v);
<1 VISITED (v):=1;
<2 Khởi tạo queue với v được nạp vào;
<3 While Q không rỗng do Begin
Call CQDELETE (v,Q); {lấy đỉnh v ra khỏi Q}
For mỗi đỉnh w lân cận với v do
If VISITED (w):=0 then Begin
Call CQINSERT (w,Q); VISITED (w):=1
End;
End;
<4.Return
[2]
1.4 Độ phức tạp tính toán của thuật toán DFS và BFS
Quá trình tìm kiếm trên đồ thị bắt đầu từ một đỉnh có thể thăm tất cả các đỉnh còn lại, khi đó cách biểu diễn đồ thị có ảnh hưởng lớn tới chi phí về thời gian thực hiện giải thuật
Ta thấy: Trong trường hợp G được biểu diễn bởi một danh sách lân cận thì đỉnh w lân cận của v sẽ được xác định bằng cách dựa vào danh sách móc nối ứng với v Vì giải thuật DFS chỉ xét mỗi nút trong danh sách lân cận nhiều nhất một lần thôi mà lại có 2e nút danh sách (ứng vơi 2e cung), nên thời gian
đề hoàn thành phép tìm kiếm là O(e)
Trang 21Còn G được biểu diễn bởi ma trận lân cận thì thời gian để xác định mọi đỉnh lân cận của v là O(n) Vì tối đa có n đỉnh được thăm, nên thời gian tìm kiếm tổng quát sẽ là O(n 2 )
Trong trường hợp ta biểu diễn đồ thị bằng danh sách kề, cả hai thuật
toán BFS và DFS đều có độ phức tạp tính toán là O(n + m) = O(max(n, m))
Đây là cách cài đặt tốt nhất Nếu ta biểu diễn đồ thị bằng ma trận kề thì
độ phức tạp tính toán trong trường hợp này là O(n + n2) = O(n2) Nếu ta biểu
diễn đồ thị bằng danh sách cạnh, thao tác duyệt những đỉnh kề với đỉnh u sẽ dẫn tới việc phải duyệt qua toàn bộ danh sách cạnh, đây là cài đặt tồi nhất, nó
có độ phức tạp tính toán là O(n.m) [7]
Trang 22CHƯƠNG 2: MỘT SỐ BÀI TOÁN TÌM KIẾM TRÊN ĐỒ THỊ
2.1 Bài toán tìm đường đi ngắn nhất trên đồ thị có trọng số
2.1.1 Phát biểu bài toán
Bài toán đó phát biểu dưới dạng tổng quát như sau: Cho đồ thị đơn, vô
hướng, có trọng số và không có chu trình âm G = (V, E,w)
Yêu cầu: Hãy tìm một đường đi cơ bản ngắn nhất (tổng trọng số qua
các đỉnh trên đường đi) từ đỉnh xuất phát x∈V đến đỉnh đích y∈V (không có
đỉnh lặp lại)
Nếu như đồ thị có chu trình âm (chu trình với độ dài âm) thì khoảng cách giữa một số cặp đỉnh nào đó có thể không xác định, bởi vì bằng cách đi vòng theo chu trình này một số lần đủ lớn, ta có thể chỉ ra đường đi giữa hai đỉnh nào đó trong chu trình này nhỏ hơn bất kỳ một số cho trước nào Trong trường hợp như vậy, có thể đặt vấn đề tìm đường đi cơ bản (đường đi không
có đỉnh lặp lại) ngắn nhất Vấn đề đó là một vấn đề hết sức phức tạp mà ta sẽ không bàn tới ở đây
Dưới đây, chúng tôi giới thiệu hai thuật toán giải bài toán này là thuật toán Ford – Bellman và thuật toán Dijkstra
2.1.2 Thuật toán Ford – Bellman
Thuật toán Ford-Bellman có thể phát biểu:
Giả sử ma trận trọng số là a(n,n): a[i,j] là độ dài đoạn đường trực tiếp nối i và j Dùng mảng một chiều v(n) để xây dựng nhãn cho các đỉnh: Mỗi đỉnh i có một nhãn là v[i] thể hiện độ dài đường đi ngắn nhất từ đỉnh xuất phát x đến i
Trang 23Thực hiện sửa nhãn n-1 lần cho các đỉnh: Trong mỗi lần sửa nhãn, nếu (i,j) là một cạnh đồ thị và v[j]>v[i] +a[i,j] thì sửa lại nhãn cho đỉnh j là v[j] :=v[i]+ a[i,j] [7]
Hay nói cách khác: Với đỉnh xuất phát S Gọi d(v) là khoảng cách từ S tới v
Ban đầu d(S) được khởi gán bằng 0 còn các d(v) với v ≠ S được khởi gán bằng +∞
Sau đó ta tối ưu hoá dần các d(v) như sau: Xét mọi cặp đỉnh u, v của đồ
thị, nếu có một cặp đỉnh u, v mà d(v) > d(u) + c(u, v) thì ta đặt lại d(v) := d(u) + c(u, v)
Tức là nếu độ dài đường đi từ S tới v lại lớn hơn tổng độ dài đường đi
từ S tới u cộng với chi phí đi từ u tới v thì ta sẽ huỷ bỏ đường đi từ S tới v đang có và coi đường đi từ S tới v chính là đường đi từ S tới u sau đó đi tiếp từ
u tới v Chú ý rằng ta đặt c[u, v] = +∞ nếu (u, v) không là cung Thuật toán sẽ kết thúc khi không thể tối ưu thêm bất kỳ một nhãn d[v] nào nữa
Tính dừng của thuật toán:
Tại bước lặp 0: Bước khởi tạo d(S) = 0; d(v) := +∞ với v ≠ S: thì dãy
d(v) chính là độ dài đường đi ngắn nhất từ S tới v đi qua không quá 0 cạnh
Giả sử tại bước lặp thứ i, d(v) bằng độ dài đường đi ngắn nhất từ S tới v không quá i cạnh, thì do tính chất: đường đi từ S tới v không quá i + 1 cạnh sẽ phải thành lập bằng cách: lấy một đường đi từ S tới một đỉnh u nào đó không quá i cạnh, rồi đi tiếp tới v bằng cung (u, v) Nên độ dài đường đi ngắn nhất từ
S tới v không quá i + 1 cạnh sẽ được tính bằng giá trị nhỏ nhất trong các giá
trị: (Nguyên lý tối ưu Bellman)
Độ dài đường đi ngắn nhất từ S tới v không quá i cạnh
Độ dài đường đi ngắn nhất từ S tới u không quá i cạnh cộng với trọng
số cạnh (u, v) (∀u)
Trang 24for u := 1 to n do for v := 1 to n do
d(v) := min(d(v), d(u) + c(u, v));
Nên sau bước lặp tối ưu các d(v) bằng công thức d(v)bước i+1 = min(d(v)bước i, d(u)bước i+ c(u, v)) thì các d(v) sẽ bằng độ dài đường đi ngắn nhất từ S tới v không quá i + 1 cạnh
Sau bước lặp tối ưu thứ n - 1, ta có d(v) = độ dài đường đi ngắn nhất từ
S tới v không quá n - 1 cạnh Vì đồ thị không có chu trình âm nên sẽ có một đường đi ngắn nhất từ S tới v là đường đi cơ bản (không quá n - 1 cạnh) Tức
là d(v) sẽ là độ dài đường đi ngắn nhất từ S tới v
Vậy thì số bước lặp tối ưu hoá sẽ không quá n - 1 bước [2]
Nếu mỗi bước ta mô tả dưới dạng:
2.1.3 Thuật toán Dijkstra
Tổ chức dữ liệu: Mảng A(n, n) thể hiện ma trận trọng số Mảng v[n] là
nhãn độ dài đường đi ngắn nhất: v[i] là độ dài đường đi ngắn nhất từ đỉnh xuất phát x tới đỉnh i d(n) mảng đánh dấu đỉnh đã có nhãn tối ưu Gán cho d[i]= True khi v[i] đã là độ dài tối ưu của đường đi từ x tới i, khi đó đỉnh i gọi
là đỉnh đã cố định nhãn d[i]=false khi v[i] chưa đạt giá trị tối ưu, khi đó đỉnh
i được gọi là đỉnh tự do Mảng Tr(n) với ý nghĩa nếu Tr[i]:=j thì đỉnh j là đỉnh ngay trước đỉnh i trên hành trình ngắn nhất
Thuật toán Dijkstra (E.Dijkstra - 1959) có thể mô tả như sau:
Bước 1: Khởi tạo
- Khởi trị đường đi ngắn nhất từ đỉnh x tới đỉnh i là v[i]:=a[x,i] (nếu không có đường đi trực tiếp từ x đến i thì a[x,i] bằng vô cùng) Lưu lại đỉnh trước khi tới i trên hành trình ngắn nhất là Tr[i]:=x
- Đánh dấu mọi đỉnh i là tự do (nhãn v[i] chưa tối ưu) bắng cách gán d[i]:=False
Trang 25- Khởi trị nhãn đỉnh x là v[x]:=0; d[x]:=True (nhãn của đỉnh x đã tối
+ Đánh dấu i0 đã được cố định nhãn: D[i0]:= True;
+ Sửa nhãn cho các đỉnh j tự do kề với i0 theo công thức
v[j]=Min{v[j],v[i0]+a[i0,j]}
+ Nếu v[j]=v[i0]+a[i0,j] thì lưu vết đỉnh trước j là i0: Tr[j]:=i0
Bước 3 Tìm và ghi kết quả
V[y] là độ dài đường đi ngắn nhất từ x đến y Lần ngược theo mảng Tr
để tìm hành trình này
2.1.4 Độ phức tạp
Thuật toán Ford-Bellman: Sau n-1 lần sửa nhãn thì v[y] chính là độ dài đường đi ngắn nhất từ x đến y Độ phức tạp của thuật toán cỡ O(n 3 )
Thuật toán Dijkstra được thực hiện bằng phương pháp gán nhãn nêu
trên có độ phức tạp thuật toán cỡ O(n 2 ) Do đó nếu số đỉnh lớn (N cỡ 5000) thì
thời gian thực hiện khá lâu, đồng thời không đủ bộ nhớ để tổ chức dữ liệu
Trang 262.2 Các thuật toán tìm kiếm trên cây khung
2.2.1 Bài toán cây khung
Khái niệm cây khung: Cho đồ thị G = (V, E) vô hướng, liên thông và T
= (V, E’) là một đồ thị con của G (E’ ⊆ E) Khi đó, T được gọi là cây khung
(cây bao trùm) nếu T liên thông và không có chu trình đơn
Cho G = (V, E, w) là đồ thị vô hướng liên thông có trọng số, với một cây khung T của G, ta gọi trọng số của cây T là tổng trọng số các cạnh trong T
Yêu cầu: Trong số các cây khung của G, chỉ ra cây khung có trọng số
nhỏ nhất
Cây khung như vậy được gọi là cây khung nhỏ nhất của đồ thị, và bài toán đó gọi là bài toán xây dựng cây khung nhỏ nhất Dưới đây ta sẽ xét một trong hai thuật toán thông dụng để giải bài toán cây khung nhỏ nhất của đơn
đồ thị vô hướng có trọng số
2.2.2 Thuật toán Prim
Một trong hai thuật toán quan trọng để giải bài toán tìm cây khung nhỏ nhất là thuật toán Prim Thuật toán đó có thể phát biểu hình thức như sau:
Đơn đồ thị vô hướng G = (V, E,w) Xét cây T trong G và một đỉnh v, gọi khoảng cách từ v tới T là trọng số nhỏ nhất trong số các cạnh nối v với
một đỉnh nào đó trong T:
d[v] = min{w[u, v] u∈T}
Ban đầu khởi tạo cây T chỉ gồm có mỗi đỉnh {1} Sau đó cứ chọn trong
số các đỉnh ngoài T ra một đỉnh gần T nhất, kết nạp đỉnh đó vào T đồng thời
kết nạp luôn cả cạnh tạo ra khoảng cách gần nhất đó Cứ làm như vậy cho tới khi:
Hoặc đã kết nạp được tất cả n đỉnh thì ta có T là cây khung nhỏ nhất
Trang 27Bước 1: Khởi tạo: T = {s} d[s] = 0, u = s (s - đỉnh xuất phát) d[v]=+∞(v ∉ T) Bước 2: Lặp N-1 lần (N số đỉnh của đồ thị):
2.1 Cập nhật các đỉnh kề với u ở ngoài T Nếu
Bước 3: In ra cây khung hoặc thông báo vô nghiệm
Hoặc chưa kết nạp được hết n đỉnh nhưng mọi đỉnh ngoài T đều có khoảng cách tới T là +∞ Khi đó đồ thị đã cho không liên thông, ta thông báo
việc tìm cây khung thất bại
Về mặt kỹ thuật cài đặt, ta có thể làm như sau:
Sử dụng mảng đánh dấu Free Free[v] = TRUE nếu như đỉnh v chưa bị kết nạp vào T
Gọi d[v] là khoảng cách từ v tới T Ban đầu khởi tạo d[1] = 0 còn d[2]
= d[3] = = d[n] = +∞ Tại mỗi bước chọn đỉnh đưa vào T, ta sẽ chọn đỉnh
u nào ngoài T và có d[u] nhỏ nhất Khi kết nạp u vào T rồi thì rõ ràng các nhãn d[v] sẽ thay đổi: d[v]mới := min(d[v]cũ, v[u, v]) Vấn đề chỉ có vậy
(chương trình rất giống thuật toán Dijkstra, chỉ khác ở công thức tối ưu nhãn)
Có thể mô tả thuật toán Prim bằng đoạn giả mã sau: [2]
2.2.3 Thuật toán Kruskal
Thuật toán Kruskal dựa trên mô hình xây dựng cây khung bằng thuật toán hợp nhất, chỉ có điều thuật toán không phải xét các cạnh với thứ tự tuỳ ý
mà xét các cạnh theo thứ tự đã sắp xếp: Với đồ thị vô hướng G = (V, E) có n
Trang 28đỉnh Khởi tạo cây T ban đầu không có cạnh nào Xét tất cả các cạnh của đồ
thị từ cạnh có trọng số nhỏ đến cạnh có trọng số lớn, nếu việc thêm cạnh đó
vào T không tạo thành chu trình đơn trong T thì kết nạp thêm cạnh đó vào T
Cứ làm như vậy cho tới khi:
Hoặc đã kết nạp được n - 1 cạnh vào trong T thì ta được T là cây khung
nhỏ nhất
Hoặc chưa kết nạp đủ n - 1 cạnh nhưng khi kết nạp thêm một cạnh bất
kỳ trong số các cạnh còn lại thì sẽ tạo thành chu trình đơn Trong trường hợp này đồ thị G là không liên thông, việc tìm kiếm cây khung thất bại
Như vậy có hai vấn đề quan trọng khi cài đặt thuật toán Kruskal:
Thứ nhất, làm thế nào để xét được các cạnh từ cạnh có trọng số nhỏ tới cạnh có trọng số lớn Ta có thể thực hiện bằng cách sắp xếp danh sách cạnh theo thứ tự không giảm của trọng số, sau đó duyệt từ đầu tới cuối danh sách cạnh
Nên sử dụng các thuật toán sắp xếp hiệu quả để đạt được tốc độ nhanh trong trường hợp số cạnh lớn Trong trường hợp tổng quát, thuật toán HeapSort là hiệu quả nhất bởi nó cho phép chọn lần lượt các cạnh từ cạnh trọng nhỏ nhất tới cạnh trọng số lớn nhất ra khỏi Heap và có thể xử lý (bỏ qua hay thêm vào cây) luôn
Thứ hai, làm thế nào kiểm tra xem việc thêm một cạnh có tạo thành chu
trình đơn trong T hay không Để ý rằng các cạnh trong T ở các bước sẽ tạo
thành một rừng (đồ thị không có chu trình đơn) Muốn thêm một cạnh (u, v)
vào T mà không tạo thành chu trình đơn thì (u, v) phải nối hai cây khác nhau của rừng T, bởi nếu u, v thuộc cùng một cây thì sẽ tạo thành chu trình đơn trong cây đó Ban đầu, ta khởi tạo rừng T gồm n cây, mỗi cây chỉ gồm đúng một đỉnh, sau đó, mỗi khi xét đến cạnh nối hai cây khác nhau của rừng T thì ta kết nạp cạnh đó vào T, đồng thời hợp nhất hai cây đó lại thành một cây [1]
Trang 292.2.4 Độ phức tạp
Xét về độ phức tạp tính toán, thuật toán Prim có độ phức tạp là O(n 2 )
Tương tự thuật toán Dijkstra, nếu kết hợp thuật toán Prim với cấu trúc Heap
sẽ được một thuật toán với độ phức tạp O((m+n)logn)
Đối với thuật toán Kruskal, ta có thể chứng minh được rằng thao tác
GetRoot có độ phức tạp là O(log2n), còn thao tác Union là O(1) Giả sử ta đã
có danh sách cạnh đã sắp xếp rồi thì xét vòng lặp dựng cây khung, nó duyệt qua danh sách cạnh và với mỗi cạnh nó gọi 2 lần thao tác GetRoot, vậy thì độ
phức tạp là O(mlog2n), nếu đồ thị có cây khung thì m≥ n-1 nên ta thấy chi phí
thời gian chủ yếu sẽ nằm ở thao tác sắp xếp danh sách cạnh bởi độ phức tạp
của HeapSort là O(mlog2m) Vậy độ phức tạp tính toán của thuật toán là O(mlog2m) trong trường hợp xấu nhất Tuy nhiên, phải lưu ý rằng để xây
dựng cây khung thì ít khi thuật toán phải duyệt toàn bộ danh sách cạnh mà chỉ
một phần của danh sách cạnh mà thôi [1]
2.3 Bài toán tìm chu trình Hamilton qua tất cả các đỉnh của đồ thị
2.3.1 Phát biểu bài toán
Khái niệm chu trình Hamilton: Cho đồ thị G = (V, E) có n đỉnh
Chu trình (x1, x2, , xn, x1) được gọi là chu trình Hamilton nếu xi≠xj với 1≤i < j≤n
Đường đi (x1, x2, , xn) được gọi là đường đi Hamilton nếu xi≠xj với 1≤i<j≤ n
Có thể phát biểu một cách hình thức: Chu trình Hamilton là chu trình
xuất phát từ 1 đỉnh, đi thăm tất cả những đỉnh còn lại mỗi đỉnh đúng 1 lần,
cuối cùng quay trở lại đỉnh xuất phát Đường đi Hamilton là đường đi qua tất
cả các đỉnh của đồ thị, mỗi đỉnh đúng 1 lần
Bài toán tìm chu trình Hamilton: Cho đồ thị G = (V, E) có n đỉnh, m
cạnh Yêu cầu: Hãy chỉ ra một chu trình Hamilton của đồ thị đã cho
Trang 30Bước 1 Khởi tạo: free[u] = true với u thuộc G
Bước 2 Visit(i): Thăm đỉnh ở bước thứ i
2.1 u = đỉnh vừa thăm xong
Xét các đỉnh v kề u và free[v] = true
2.2 Ghi nhận đỉnh ở bước thứ i
2.3 Nếu (i =n) và (u kề với 1) đến Bước 3
2.4 Đánh dấu thăm v và Vitsit(i+1);
Bước 3 Truy vết tìm đường đi (nếu có)
Để tìm chu trình Hamilton, ta quan tâm đến các định lý sau:
Định lý 1: Đồ thị vô hướng G, trong đó tồn tại k đỉnh sao cho nếu xoá
đi k đỉnh này cùng với những cạnh liên thuộc của chúng thì đồ thị nhận được
sẽ có nhiều hơn k thành phần liên thông Thì khẳng định là G không có chu
trình Hamilton Mệnh đề phản đảo của định lý này cho ta điều kiện cần để một đồ thị có chu trình Hamilton
Định lý 2 (Định lý Dirac (1952)): Đồ thị vô hướng G có n đỉnh (n≥3) Khi đó nếu mọi đỉnh v của G đều có deg(v)≥n/2 thì G có chu trình Hamilton
Đây là một điều kiện đủ để một đồ thị có chu trình Hamilton
Định lý 3: Đồ thị có hướng G liên thông mạnh và có n đỉnh Nếu deg(v) + ≥n/2 và deg(v) - ≥n/2 với mọi đỉnh v thì G có chu trình Hamiton [2]
2.3.2 Giới thiệu thuật toán tìm chu trình Hamilton
Dưới đây ta sẽ cài đặt một chương trình liệt kê tất cả các chu trình Hamilton xuất phát từ đỉnh 1, các chu trình Hamilton khác có thể có được bằng cách hoán vị vòng quanh Lưu ý rằng cho tới nay, người ta vẫn chưa tìm
ra một phương pháp nào thực sự hiệu quả hơn phương pháp quay lui để tìm
dù chỉ một chu trình Hamilton cũng như đường đi Hamilton trong trường hợp
đồ thị tổng quát
Trang 31CHƯƠNG 3: ỨNG DỤNG THUẬT TOÁN TÌM KIẾM TRÊN
ĐỒ THỊ ĐỂ GIẢI MỘT SỐ BÀI TOÁN HSG
Công tác bồi dưỡng học sinh giỏi môn Tin học trong nhà trường THPT hiện nay là nhiệm vụ nặng đề và vất vả của giáo viên phụ trách đội tuyển Một yếu tố rất quan trọng để có được kết quả cao là tài liệu tham khảo cho mỗi giáo viên và học sinh Một nội dung thường gặp và thường rất khó trong các
đề thi HSG, nhất là HSG Quốc gia đó là bài toán tìm kiếm bằng phương pháp
đồ thị Việc hiểu rõ và vận dụng tốt phương pháp tìm kiếm trên đồ thị sẽ góp phần thắng lợi cho mối kì thi của học sinh THPT
3.1 Ứng dụng giải bài toán
3.1.1 Bài toán đi xem world cup 2018 bằng xe buýt
Bài toán 3.1: Để phục cho world cup 2018, nước Nga đã xây dựng hệ
thống xe buýt đi lại giữa một số sân bóng, các tuyến xe buýt đều là đường đi
hai chiều Các trận đấu bóng đá của world cup được tổ chức ở N địa điểm, các địa điểm được đánh số từ 1 đến N Các trạm dừng xe buýt đều được đặt ở các
địa điểm tổ chức world cup, tất nhiên vì điều kiện không cho phép nên có một
số địa điểm không có trạm xe buýt và giữa một số địa điểm không thể đi lại bằng xe buýt được Yêu cầu hãy giúp vận động viên cũng như khán giả đến
cổ vũ world cup xác định được từ địa điểm X đến địa điểm Y nào đó có thể đi
được bằng xe buýt không?
Dữ liệu vào: Từ file văn bản XEBUYT.INP gồm:
- Dòng đầu tiên là số N (1<=N<=100);
- Dòng thứ hai ghi hai số nguyên thể hiện địa điểm X và Y
- Các dòng tiếp theo mỗi dòng ghi hai số i và j cho biết tuyến xe buýt giữa 2 địa điểm i và j
Trang 32Kết quả: Ghi ra file văn bản XEBUYT.OUT nếu không đi được từ X đến B ghi ‘Khong’, nếu được ghi ‘co’
Ví dụ với bộ dữ liệu vào và kết quả ra của bài toán như sau :
XEBUYT.INP XEBUYT.OUT XEBUYT.INP XEBUYT.OUT
Trang 33while not eof(f1) do
Trang 34else write(f2,'co');
close(f1);
close(f2);
END
Kết quả chạy chương trình với bộ dữ liệu vào như ví dụ trên
3.1.2 Bài toán đường hầm
Bài toán 3.2: Có N hòn đảo đánh số từ 1 đến N Một số hòn đảo đã có
đường hầm thông với nhau Người ta muốn xây dựng thêm một số đường hầm sao cho có thể đi lại giữa 2 hòn đảo bất kỳ bằng đường hầm Biết rằng đường hầm nối các đảo là đường đi 2 chiều, hãy lập trình tính số đường hầm
Kết quả: Ghi ra file văn bản HAM.OUT chỉ một số duy nhất cho biết số
đường hầm ít nhất cần xây dựng thêm
Trang 35Ví dụ với bộ dữ liệu vào và kết quả ra của bài toán như sau :
Trang 36Kết quả chạy chương trình với bộ dữ liệu vào như ví dụ trên
3.1.3 Đếm nhóm bạn trong Hội trại
Trang 37Bài toán 3.3: Trong một Hội trại hè do Tỉnh Đoàn tổ chức, có N học
sinh tham gia Trong đó có một số học sinh quen nhau Một số học sinh được gọi là cùng 1 nhóm bạn, nếu bất kì một học sinh nào thuộc nhóm đều có quen
ít nhất 1 học sinh khác trong cùng nhóm đó
Yêu cầu: Hãy đếm xem có bao nhiêu nhóm bạn trong N học sinh tham
gia Hội trại (Đề HSG lớp 12 Quảng Bình 2012 - 2013)
Dữ liệu vào: Từ file văn bản NHOMBAN.INP, có cấu trúc như sau: Dòng 1: Ghi số nguyên dương N, là số lượng học sinh tham gia Hội trại (1 ≤ N ≤ 100)
Trong N dòng tiếp theo: Mỗi dòng ghi N số nguyên dương a[i,j] với ý
nghĩa:
a[i,j] = 1 nếu học sinh i quen học sinh j (với i ≠j)
a[i,j] = 0 nếu học sinh i không quen học sinh j (với i ≠j)
a[i,i] = 1 (học sinh i được xem là quen bản thân nó)
Các số trên cùng một dòng được ghi cách nhau ít nhất một dấu cách
Kết quả: Ghi ra file văn bản NHOMBAN.OUT, theo cấu trúc như sau: Dòng 1: Ghi số nguyên dương K, là số lượng nhóm bạn tìm được trong
N học sinh tham gia Hội trại
Ví dụ với bộ dữ liệu vào và kết quả ra của bài toán như sau