BÀI GIẢNG GIẢI THUẬT VÀ LẬP TRÌNH - QUY HOẠCH ĐỘNG - LÊ MINH HOÀNG - 8 docx

36 545 1
BÀI GIẢNG GIẢI THUẬT VÀ LẬP TRÌNH - QUY HOẠCH ĐỘNG - LÊ MINH HOÀNG - 8 docx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Các thuật toán đồ thị begin Pop := heap[1]; {Nút gốc Heap chứa đỉnh có nhãn tự nhỏ nhất} v := heap[nHeap]; {v đỉnh nút cuồi Heap, đảo lên đầu vun đống} Dec(nHeap); r := 1; {Bắt đầu từ nút gốc} while r * c[u, k] + c[k, v] then {Đường từ qua k tốt hơn} begin c[u, v] := c[u, k] + c[k, v]; {Ghi nhận đường thay cho đường cũ} Trace[u, v] := Trace[u, k]; {Lưu vết đường đi} end; end; procedure PrintResult; {In đường từ S tới F} var fo: Text; begin Assign(fo, OutputFile); Rewrite(fo); if c[S, F] = maxC then WriteLn(fo, 'Path from ', S, ' to ', F, ' not found') else begin WriteLn(fo, 'Distance from ', S, ' to ', F, ': ', c[S, F]); repeat Write(fo, S, '->'); S := Trace[S, F]; {Nhắc lại Trace[S, F] đỉnh liền sau S đường tới F} until S = F; WriteLn(fo, F); end; Close(fo); end; begin LoadGraph; Floyd; PrintResult; end Khác biệt rõ ràng thuật toán Floyd cần tìm đường ngắn cặp đỉnh khác, chương trình việc in kết khơng phải thực lại thuật tốn Floyd Đại học Sư phạm Hà Nội, 1999-2002 Các thuật toán đồ thị 245 8.8 NHẬN XÉT Bài toán đường dài đồ thị số trường hợp giải cách đổi dấu trọng số tất cung tìm đường ngắn nhất, cẩn thận, xảy trường hợp có chu trình âm Trong tất cài đặt trên, sử dụng ma trận trọng số khơng sử dụng danh sách cạnh hay danh sách kề có trọng số, nên ta đưa đồ thị đầy đủ đem trọng số +∞ gán cho cạnh khơng có đồ thị ban đầu Trên máy tính khơng có khái niệm trừu tượng +∞ nên ta phải chọn số dương đủ lớn để thay Như đủ lớn? số phải đủ lớn tất trọng số đường dù đường thật có tồi tệ đến đâu tốt đường trực cạnh tưởng tượng Vậy nên đồ thị cho số đỉnh trọng số cạnh vào cỡ 300 chẳng hạn giá trị khơng thể chọn phạm vi Integer hay Word Ma trận c phải khai báo ma trận LongInt giá trị số maxC chương trình phải đổi lại 300 * 299 + - điều gây nhiều phiền tối, chẳng hạn vấn đề lãng phí nhớ Để khắc phục, người ta cài đặt danh sách kề kèm trọng số sử dụng kỹ thuật đánh dấu khéo léo trường hợp cụ thể Tuy nhiên có điều chắn: đồ thị cho số đỉnh trọng số cạnh vào khoảng 300 trọng số c[u, v] thuật toán Floyd nhãn d[v] ba thuật toán cịn lại chắn khơng thể khai báo Integer Xét độ phức tạp tính tốn, cài đặt trên, thuật tốn Ford-Bellman có độ phức tạp O(n3), thuật toán Dijkstra O(n2), thuật toán tối ưu nhãn theo thứ tự tơpơ O(n2) cịn thuật toán Floyd O(n3) Tuy nhiên sử dụng danh sách kề, thuật toán tối ưu nhãn theo thứ tự tơpơ có độ phức tạp tính tốn O(m) Thuật toán Dijkstra kết hợp với cấu trúc liệu Heap có độ phức tạp O(max(n, m).logn) Khác với tốn đại số hay hình học có nhiều cách giải cần nắm vững cách coi đạt u cầu, thuật tốn tìm đường ngắn bộc lộ rõ ưu, nhược điểm trường hợp cụ thể (Ví dụ số đỉnh đồ thị lớn làm cho biểu diễn ma trận trọng số thuật tốn Floyd gặp khó khăn, hay thuật tốn Ford-Bellman làm việc chậm) Vì yêu cầu trước tiên phải hiểu chất thành thạo việc cài đặt tất thuật tốn để sử dụng chúng cách uyển chuyển trường hợp cụ thể Những tập sau cho ta thấy rõ điều Bài tập Bài Giải thích đồ thị sau, cần tìm đường dài từ đỉnh tới đỉnh lại khơng thể dùng thuật tốn Dijkstra được, thử áp dụng thuật toán Dijkstra theo bước xem sao: Lê Minh Hoàng 246 Chuyên đề 2 2 4 Bài Trên mặt phẳng cho n đường tròn (n ≤ 2000), đường tròn thứ i cho ba số thực (Xi, Yi, Ri), (Xi, Yi) toạ độ tâm Ri bán kính Chi phí di chuyển đường trịn Chi phí di chuyển hai đường trịn khoảng cách chúng Hãy tìm phương án di chuyển hai đường tròn S, F cho trước với chi phí Bài Cho dãy n số nguyên A[1], A[2], …, A[n] (n ≤ 10000; ≤ A[i] ≤ 10000) Hãy tìm dãy gồm nhiều phần tử dãy cho mà tổng hai phần tử liên tiếp số nguyên tố Bài Một cơng trình lớn chia làm n công đoạn đánh số 1, 2, …, n Công đoạn i phải thực thời gian t[i] Quan hệ công đoạn cho bảng a[i, j]: a[i, j] = TRUE ⇔ công đoạn j bắt đầu mà công việc i xong Hai cơng đoạn độc lập tiến hành song song, bố trí lịch thực cơng đoạn cho thời gian hồn thành cơng trình sớm nhất, cho biết thời gian sớm Bài Cho bảng số tự nhiên kích thước mxn (1 ≤ m, n ≤ 100) Từ ô di chuyển sang kề cạnh với Hãy tìm cách từ (x, y) ô biên cho tổng số ghi ô qua cực tiểu Đại học Sư phạm Hà Nội, 1999-2002 Các thuật toán đồ thị 247 §9 BÀI TỐN CÂY KHUNG NHỎ NHẤT 9.1 BÀI TOÁN CÂY KHUNG NHỎ NHẤT Cho G = (V, E) đồ thị vơ hướng liên thơng có trọng số, với khung T G, ta gọi trọng số T tổng trọng số cạnh T Bài toán đặt số khung G, khung có trọng số nhỏ nhất, khung gọi khung (hay bao trùm) nhỏ đồ thị, tốn gọi tốn khung nhỏ Sau ta xét hai thuật tốn thơng dụng để giải tốn khung nhỏ đơn đồ thị vơ hướng có trọng số Input: file văn MINTREE.INP: • Dịng 1: Ghi hai số số đỉnh n (≤ 100) số cạnh m đồ thị cách dấu cách • m dịng tiếp theo, dịng có dạng số u, v, c[u, v] cách dấu cách thể đồ thị có cạnh (u, v) trọng số cạnh c[u, v] (c[u, v] số ngun có giá trị tuyệt đối khơng 100) Output: file văn MINTREE.OUT ghi cạnh thuộc khung trọng số khung 1 2 1 MINTREE.INP 69 121 131 241 232 251 351 361 452 562 MINTREE.OUT Minimal spanning tree: (2, 4) = (3, 6) = (2, 5) = (1, 3) = (1, 2) = Weight = 9.2 THUẬT TOÁN KRUSKAL (JOSEPH KRUSKAL - 1956) Thuật tốn Kruskal dựa mơ hình xây dựng khung thuật tốn hợp (§5), có điều thuật tốn khơng phải xét cạnh với thứ tự tuỳ ý mà xét cạnh theo thứ tự xếp: Với đồ thị vô hướng G = (V, E) có n đỉnh Khởi tạo T ban đầu khơng có cạnh Xét tất cạnh đồ thị từ cạnh có trọng số nhỏ đến cạnh có trọng số lớn, việc thêm cạnh vào T khơng tạo thành chu trình đơn T kết nạp thêm cạnh vào T Cứ làm khi: Hoặc kết nạp n - cạnh vào T ta T khung nhỏ Hoặc chưa kết nạp đủ n - cạnh kết nạp thêm cạnh số cạnh cịn lại tạo thành chu trình đơn Trong trường hợp đồ thị G khơng liên thơng, việc tìm kiếm khung thất bại Như có hai vấn đề quan trọng cài đặt thuật toán Kruskal: Lê Minh Hoàng 248 Chuyên đề Thứ nhất, làm để xét cạnh từ cạnh có trọng số nhỏ tới cạnh có trọng số lớn Ta thực cách xếp danh sách cạnh theo thứ tự khơng giảm trọng số, sau duyệt từ đầu tới cuối danh sách cạnh Trong trường hợp tổng qt, thuật tốn HeapSort hiệu cho phép chọn cạnh từ cạnh trọng nhỏ tới cạnh trọng số lớn khỏi Heap xử lý (bỏ qua hay thêm vào cây) Thứ hai, làm kiểm tra xem việc thêm cạnh có tạo thành chu trình đơn T hay không Để ý cạnh T bước tạo thành rừng (đồ thị khơng có chu trình đơn) Muốn thêm cạnh (u, v) vào T mà không tạo thành chu trình đơn (u, v) phải nối hai khác rừng T, u, v thuộc tạo thành chu trình đơn Ban đầu, ta khởi tạo rừng T gồm n cây, gồm đỉnh, sau đó, xét đến cạnh nối hai khác rừng T ta kết nạp cạnh vào T, đồng thời hợp hai lại thành Nếu cho đỉnh v nhãn Lab[v] số hiệu đỉnh cha đỉnh v cây, trường hợp v gốc Lab[v] gán giá trị âm Khi ta hồn tồn xác định gốc chứa đỉnh v hàm GetRoot đây: function GetRoot(v∈V): ∈V; begin while Lab[v] > v := Lab[v]; GetRoot := v; end; Vậy để kiểm tra cạnh (u, v) có nối hai khác rừng T hay khơng? ta kiểm tra GetRoot(u) có khác GetRoot(v) hay khơng, có gốc Để hợp gốc r1 gốc r2 thành cây, ta lưu ý dùng để ghi nhận tập hợp đỉnh thuộc cấu trúc cạnh khơng quan trọng Vậy để hợp gốc r1 gốc r2, ta việc coi r1 nút cha r2 cách đặt: Lab[r2] := r1 r1 r1 r2 r2 u u v v Hình 77: Hai gốc r1 r2 hợp chúng Đại học Sư phạm Hà Nội, 1999-2002 260 Chuyên đề u, v: Integer; begin for u := to n for v := to n cf[u, v] := maxC; for u := to n for v := to n if c[u, v] > then {Nếu u, v cung mạng} begin if f[u, v] < c[u, v] then cf[u, v] := c[u, v] - f[u, v]; {Đặt cung thuận} if f[u, v] > then cf[v, u] := -f[u, v]; {Đặt cung nghịch} end; end; {Thủ tục tìm đường từ A tới B BFS, trả TRUE có đường, FALSE khơng có đường} function FindPath: Boolean; var Queue: array[1 max] of Integer; {Hàng đợi dùng cho BFS} Free: array[1 max] of Boolean; u, v, First, Last: Integer; begin FillChar(Free, SizeOf(Free), True); First := 1; Last := 1; Queue[1] := A; {Queue gồm đỉnh phát A} Free[A] := False; {đánh dấu A} repeat u := Queue[First]; Inc(First); {Lấy u khỏi Queue} for v := to n if Free[v] and (cf[u, v] maxC) then {Xét v chưa đánh dấu kề với u} begin Trace[v] := u; {Lưu vết đường A → … → u → v} if v = B then {v = B ta có đường từ A tới B, thoát thủ tục} begin FindPath := True; Exit; end; Free[v] := False; {đánh dấu v} Inc(Last); Queue[Last] := v; {Queue ← v} end; until First > Last; {Queue rỗng} FindPath := False; {ở không Exit tức khơng có đường} end; {Thủ tục tăng luồng dọc theo đường tăng luồng tìm FindPath} procedure IncFlow; var u, v, IncValue: Integer; begin {Trước hết dị đường theo vết để tìm trọng số nhỏ cung đường} IncValue := maxC; v := B; while v A begin u := Trace[v]; {Để ý ⏐cf[u, v]⏐ trọng số cung (u, v) đồ thị tăng luồng} if Abs(cf[u, v]) < IncValue then IncValue := Abs(cf[u, v]); v:= u; end; {Dò lại đường lần thứ hai, lần để tăng luồng} v := B; while v A begin u := Trace[v]; if cf[u, v] > then f[u, v] := f[u, v] + IncValue {Nếu (u, v) cung thuận Gf} else f[v, u] := f[v, u] - IncValue; {Nếu (u, v) cung nghịch Gf} v := u; end; Đại học Sư phạm Hà Nội, 1999-2002 Các thuật toán đồ thị 261 end; procedure PrintResult; {In luồng cực đại tìm được} var u, v, m: Integer; fo: Text; begin Assign(fo, OutputFile); Rewrite(fo); m := 0; for u := to n for v := to n if c[u, v] > then {Nếu có cung (u, v) mạng in giá trị luồng f gán cho cung đó} begin WriteLn(fo, 'f(', u, ', ', v, ') = ', f[u, v]); if u = A then m := m + f[A, v]; {Giá trị luồng cực đại = tổng luồng phát từ A} end; WriteLn(fo, 'Max Flow: ', m); Close(fo); end; begin Enter; {Nhập liệu} FillChar(f, SizeOf(f), 0); {Khởi tạo luồng 0} repeat {Bước lặp} CreateGf; {Dựng đồ thị tăng luồng} if not FindPath then Break; {Nếu khơng tìm đường tăng luồng ngay} IncFlow; {Tăng luồng dọc đường tăng luồng} until False; PrintResult; end Bây ta thử xem cách làm chỗ chưa hay chỗ ? Trước hết, thuật tốn tìm đường Breadth First Search tốt, người ta chứng minh đường tăng luồng tìm BFS làm giảm đáng kể số bước lặp tăng luồng so với DFS Nhưng thấy việc xây dựng tường minh đồ thị Gf thông qua việc xây dựng ma trận cf để làm việc tìm đường lãng phí, cần dựa vào ma trận khả thơng qua c luồng f có ta biết (u, v) có phải cung đồ thị tăng luồng Gf hay không Thứ hai, bước tăng luồng, ta phải dò lại hai lần đường đi, lần để tìm trọng số nhỏ cung đường, lần để tăng luồng Trong việc tìm trọng số nhỏ cung đường kết hợp làm thủ tục tìm đường cách sau: Đặt Delta[v] trọng số nhỏ cung đường từ A tới v, khởi tạo Delta[A] = +∞ Tại bước từ đỉnh u thăm đỉnh v BFS, Delta[v] tính giá trị nhỏ hai giá trị Delta[u] trọng số cung (u, v) đồ thị tăng luồng Khi tìm đường từ A tới B Delta[B] cho ta trọng số nhỏ cung đường tăng luồng Thứ ba, bước tìm đường tăng luồng, ta xác định cung cung thuận, cung cung nghịch Vì từ đỉnh u thăm đỉnh v BFS, ta lưu vết đường Trace[v] := u, sau đổi dấu Trace[v] (u, v) cung nghịch Những cải tiến cho ta cách cài đặt hiệu hơn, là: Lê Minh Hồng 262 Chun đề 10.4 THUẬT TỐN FORD - FULKERSON (L.R.FORD & D.R.FULKERSON - 1962) Mỗi đỉnh v gán nhãn (Trace[v], Delta[v]) Trong ⏐Trace[v]⏐ đỉnh liền trước v đường từ A tới v, Trace[v] âm hay dương tuỳ theo (⏐Trace[v]⏐, v) cung nghịch hay cung thuận đồ thị tăng luồng, Delta[v] trọng số nhỏ cung đường từ A tới v đồ thị tăng luồng Bước lặp tìm đường từ A tới B đồ thị tăng luồng đồng thời tính ln nhãn (Trace[v], Delta[v]) Sau tăng luồng dọc theo đường tăng luồng tìm thấy P_4_10_2.PAS * Thuật tốn Ford-Fulkerson program Max_Flow_by_Ford_Fulkerson; const InputFile = 'MAXFLOW.INP'; OutputFile = 'MAXFLOW.OUT'; max = 100; maxC = 10000; var c, f: array[1 max, max] of Integer; Trace: array[1 max] of Integer; Delta: array[1 max] of Integer; n, A, B: Integer; procedure Enter; {Nhập liệu} var m, i, u, v: Integer; fi: Text; begin Assign(fi, InputFile); Reset(fi); FillChar(c, SizeOf(c), 0); ReadLn(fi, n, m, A, B); for i := to m ReadLn(fi, u, v, c[u, v]); Close(fi); end; function Min(X, Y: Integer): Integer; begin if X < Y then Min := X else Min := Y; end; function FindPath: Boolean; var u, v: Integer; Queue: array[1 max] of Integer; First, Last: Integer; begin FillChar(Trace, SizeOf(Trace), 0); {Trace[v] = đồng nghĩa với v chưa đánh dấu} First := 1; Last := 1; Queue[1] := A; Trace[A] := n + 1; {Chỉ cần khác để đánh dấu mà thơi, số dương cả} Delta[A] := maxC; {Khởi tạo nhãn} repeat u := Queue[First]; Inc(First); {Lấy u khỏi Queue} for v := to n if Trace[v] = then {Xét nhứng đỉnh v chưa đánh dấu thăm} begin if f[u, v] < c[u, v] then {Nếu (u, v) cung thuận Gf có trọng số c[u, v] - f[u, v]} Đại học Sư phạm Hà Nội, 1999-2002 Các thuật toán đồ thị begin Trace[v] := u; {Lưu vết, Trace[v] mang dấu dương} Delta[v] := min(Delta[u], c[u, v] - f[u, v]); end else if f[v, u] > then {Nếu (u, v) cung nghịch Gf có trọng số f[v, u]} begin Trace[v] := -u; {Lưu vết, Trace[v] mang dấu âm} Delta[v] := min(Delta[u], f[v, u]); end; if Trace[v] then {Trace[v] khác tức từ u thăm v} begin if v = B then {Có đường tăng luồng từ A tới B} begin FindPath := True; Exit; end; Inc(Last); Queue[Last] := v; {Đưa v vào Queue} end; end; until First > Last; {Hàng đợi Queue rỗng} FindPath := False; {ở không Exit tức đường} end; procedure IncFlow; {Tăng luồng dọc đường tăng luồng} var IncValue, u, v: Integer; begin IncValue := Delta[B]; {Nhãn Delta[B] trọng số nhỏ cung đường tăng luồng} v := B; {Truy vết đường đi, tăng luồng dọc theo đường đi} repeat u := Trace[v]; {Xét cung (⏐u⏐, v) đường tăng luồng} if u > then f[u, v] := f[u, v] + IncValue {(|u|, v) cung thuận tăng f[u, v]} else begin u := -u; f[v, u] := f[v, u] - IncValue; {(|u|, v) cung nghịch giảm f[v, |u|]} end; v := u; until v = A; end; procedure PrintResult; {In kết quả} var u, v, m: Integer; fo: Text; begin Assign(fo, OutputFile); Rewrite(fo); m := 0; for u := to n for v := to n if c[u, v] > then begin WriteLn(fo, 'f(', u, ', ', v, ') = ', f[u, v]); if u = A then m := m + f[A, v]; end; WriteLn(fo, 'Max Flow: ', m); Close(fo); end; begin Enter; FillChar(f, SizeOf(f), 0); repeat if not FindPath then Break; Lê Minh Hoàng 263 264 Chuyên đề IncFlow; until False; PrintResult; end Định lý luồng cực đại mạng lát cắt hẹp nhất: Luồng cực đại mạng khả thông qua lát cắt hẹp Khi tìm luồng cực đại theo thuật tốn khơng có đường từ A tới B đồ thị tăng luồng Nếu đặt tập X gồm đỉnh đến từ đỉnh phát A đồ thị tăng luồng (tất nhiên A∈X) tập Y gồm đỉnh lại (tất nhiên B∈Y) (X, Y) lát cắt hẹp Có thể có nhiều lát cắt hẹp nhất, ví dụ đặt tập Y gồm đỉnh đến đỉnh thu B đồ thị tăng luồng (tất nhiên B∈ Y) tập X gồm đỉnh cịn lại (X, Y) lát cắt hẹp Định lý tính ngun: Nếu tất khả thơng qua số ngun thuật tốn ln tìm luồng cực đại với luồng cung số nguyên Điều chứng minh dễ ban đầu khởi tạo luồng tức luồng cung nguyên Mỗi lần tăng luồng lên lượng trọng số nhỏ cung đường tăng luồng số nguyên nên cuối luồng cực đại tất phải có luồng cung nguyên Định lý chi phí thời gian thực giải thuật: Trong phương pháp Ford-Fulkerson, dùng đường ngắn (qua cạnh nhất) từ đỉnh phát tới đỉnh thu đồ thị tăng luồng cần n.m lần chọn đường để tìm luồng cực đại Edmonds Karp chứng minh tính chất đề nghị phương pháp cải tiến: Tại bước, ta nên tìm đường tăng luồng cho giá trị tăng luồng gia tăng nhiều Nói chung thuật tốn Ford-Fulkerson, đánh giá lý thuyết bị lệch nhiều so với thực tế, với phân tích trường hợp xấu, chi phí thời gian thực thuật tốn lớn Nhưng thực tế thuật toán hoạt động nhanh hiệu Bài tập: Bài Mạng với nhiều điểm phát nhiều điểm thu: Cho mạng gồm n đỉnh với p điểm phát A1, A2, …, Ap q điểm thu B1, B2, …, Bq Mỗi cung mạng gán khả thông qua số nguyên Các đỉnh phát có cung đỉnh thu có cung vào Một luồng mạng phép gán cho cung số thực gọi luồng cung khơng vượt q khả thông qua thoả mãn với đỉnh đỉnh phát hay đỉnh thu tổng luồng vào tổng luồng Giá trị luồng tổng luồng từ đỉnh phát = tổng luồng vào đỉnh thu Hãy tìm luồng cực đại mạng Bài Mạng với khả thông qua đỉnh cung: Cho mạng với đỉnh phát A đỉnh thu B Mỗi cung (u, v) gán khả thông qua c[u, v] Mỗi đỉnh v khác với A B gán Đại học Sư phạm Hà Nội, 1999-2002 Các thuật toán đồ thị 265 khả thông qua d[v] Một luồng mạng định nghĩa trước thêm điều kiện: tổng luồng vào đỉnh v không vượt q khả thơng qua d[v] đỉnh Hãy tìm luồng cực đại mạng Bài Lát cắt hẹp nhất: Cho đồ thị gồm n đỉnh m cạnh đỉnh A, B Hãy tìm cách bỏ số cạnh để khơng đường từ A tới B Bài Một lớp học có n bạn nam, n bạn nữ Cho m quà lưu niệm, (n ≤ m) Mỗi bạn có sở thích số q Hãy tìm cách phân cho bạn nam tặng quà cho bạn nữ thoả mãn: Mỗi bạn nam tặng quà cho bạn nữ Mỗi bạn nữ nhận quà bạn nam Bạn nam tặng quà bạn nữ nhận quà, quà phải hợp sở thích hai người Món q bạn nam chọn bạn nam khác khơng chọn Lê Minh Hồng 266 Chun đề §11 BÀI TỐN TÌM BỘ GHÉP CỰC ĐẠI TRÊN ĐỒ THỊ HAI PHÍA 11.1 ĐỒ THỊ HAI PHÍA (BIPARTITE GRAPH) Các tên gọi đồ thị hai phía dạng đơn đồ thị vô hướng G = (V, E) mà tập đỉnh chia làm hai tập X, Y rời cho cạnh đồ thị nối đỉnh X với đỉnh thuộc Y Khi người ta cịn ký hiệu G (X∪Y, E) gọi tập (chẳng hạn tập X) tập đỉnh trái tập lại tập đỉnh phải đồ thị hai phía G Các đỉnh thuộc X cịn gọi X_đỉnh, đỉnh thuộc Y gọi Y_đỉnh X Y Hình 81: Đồ thị hai phía Để kiểm tra đồ thị liên thơng có phải đồ thị hai phía hay khơng, ta áp dụng thuật toán sau: Với đỉnh v bất kỳ: X := {v}; Y := ∅; repeat Y := Y ∪ Kề(X); X := X ∪ Kề(Y); until (X∩Y ≠ ∅) or (X Y tối đại - không bổ sung nữa); if X∩Y ≠ ∅ then < Không phải đồ thị hai phía > else ; Đồ thị hai phía gặp nhiều mơ hình thực tế Chẳng hạn quan hệ hôn nhân tập người đàn ông tập người đàn bà, việc sinh viên chọn trường, thầy giáo chọn tiết dạy thời khoá biểu v.v… 11.2 BÀI TỐN GHÉP ĐƠI KHƠNG TRỌNG VÀ CÁC KHÁI NIỆM Cho đồ thị hai phía G = (X∪Y, E) X tập đỉnh trái Y tập đỉnh phải G Một ghép (matching) G tập hợp cạnh G đơi khơng có đỉnh chung Bài tốn ghép đơi (matching problem) tìm ghép lớn (nghĩa có số cạnh lớn nhất) G Xét ghép M G Các đỉnh M gọi đỉnh ghép (matched vertices), đỉnh khác chưa ghép Các cạnh M gọi cạnh ghép, cạnh khác chưa ghép Đại học Sư phạm Hà Nội, 1999-2002 Các thuật toán đồ thị 267 Nếu định hướng lại cạnh đồ thị thành cung, cạnh chưa ghép định hướng từ X sang Y, cạnh ghép định hướng từ Y X Trên đồ thị định hướng đó: Một đường xuất phát từ X_đỉnh chưa ghép gọi đường pha, đường từ X_đỉnh chưa ghép tới Y_đỉnh chưa ghép gọi đường mở Một cách dễ hiểu, quan niệm sau: Một đường pha (alternating path) đường đơn G bắt đầu X_đỉnh chưa ghép, theo cạnh chưa ghép sang Y, đến cạnh ghép X, lại đến cạnh chưa ghép sang Y… xen kẽ Một đường mở (augmenting path) đường pha Bắt đầu từ X_đỉnh chưa ghép kết thúc Y_đỉnh chưa ghép Ví dụ: với đồ thị hai phía hình Hình 82 ghép M = {(X1, Y1), (X2, Y2)} X3 Y3 đỉnh chưa ghép, đỉnh khác ghép Đường (X3, Y2, X2, Y1) đường pha Đường (X3, Y2, X2, Y1, X1, Y3) đường mở 1 2 3 X Y Hình 82: Đồ thị hai phía ghép M 11.3 THUẬT TỐN ĐƯỜNG MỞ Thuật tốn đường mở để tìm ghép lớn phát biểu sau: Bắt đầu từ ghép M (thông thường ghép khởi gán ghép rỗng hay tìm thuật tốn tham lam) Sau tìm đường mở, tìm mở rộng ghép M sau: Trên đường mở, loại bỏ cạnh ghép khỏi M thêm vào M cạnh chưa ghép Nếu khơng tìm đường mở ghép thời lớn ; while ; Như ví dụ trên, với ghép hai cạnh M = {(X1, Y1), (X2, Y2)} đường mở tìm gồm cạnh: (X3, Y2) ∉ M Lê Minh Hoàng 268 Chuyên đề (Y2, X2) ∈ M (X2, Y1) ∉ M (Y1, X1) ∈ M (X1, Y3) ∉ M Vậy ta loại cạnh (Y2, X2) (Y1, X1) ghép cũ thêm vào cạnh (X3, Y2), (X2, Y1), (X1, Y3) ghép cạnh 11.4 CÀI ĐẶT 11.4.1 Biểu diễn đồ thị hai phía Giả sử đồ thị hai phía G = (X∪Y, E) có X_đỉnh ký hiệu X[1], X[2], …, X[m] Y_đỉnh ký hiệu Y[1], Y[2], …, Y[n] Ta biểu diễn đồ thị hai phía ma trận A cỡ mxn Trong đó: A[i, j] = TRUE ⇔ có cạnh nối đỉnh X[i] với đỉnh Y[j] 11.4.2 Biểu diễn ghép Để biểu diễn ghép, ta sử dụng hai mảng: matchX[1 m] matchY[1 n] matchX[i] đỉnh thuộc tập Y ghép với đỉnh X[i] matchY[j] đỉnh thuộc tập X ghép với đỉnh Y[j] Tức cạnh (X[i], Y[j]) thuộc ghép matchX[i] = j matchY[j] = i Quy ước rằng: Nếu X[i] chưa ghép với đỉnh tập Y matchX[i] = Nếu Y[j] chưa ghép với đỉnh tập X matchY[j] = Để thêm cạnh (X[i], Y[j]) vào ghép ta việc đặt matchX[i] := j matchY[j] := i; Để loại cạnh (X[i], Y[j]) khỏi ghép ta việc đặt matchX[i] := matchY[j] := 0; 11.4.3 Tìm đường mở Vì đường mở X_đỉnh chưa ghép, theo cạnh chưa ghép sang tập Y, theo ghép để tập X, lại cạnh chưa ghép sang tập Y … cuối cạnh chưa ghép tới Y_đỉnh chưa ghép Nên thấy độ dài đường mở lẻ đường mở số cạnh ∈ M số cạnh ∉ M cạnh Và dễ thấy giải thuật tìm đường mở nên sử dụng thuật tốn tìm kiếm theo chiều rộng để đường mở tìm đường ngắn nhất, giảm bớt công việc cho bước tăng cặp ghép Ta khởi tạo hàng đợi (Queue) ban đầu chứa tất X_đỉnh chưa ghép Thuật tốn tìm kiếm theo chiều rộng làm việc theo nguyên tắc lấy đỉnh v khỏi Queue lại đẩy Queue nối từ v chưa thăm Như thăm tới Y_đỉnh chưa ghép tức ta tìm đường mở kết thúc Đại học Sư phạm Hà Nội, 1999-2002 Các thuật toán đồ thị 269 Y_đỉnh chưa ghép đó, q trình tìm kiếm dừng Còn ta thăm tới đỉnh j ∈ Y ghép, dựa vào kiện: từ j tới matchY[j] theo cạnh ghép định hướng ngược từ Y X, nên ta đánh dấu thăm j, thăm matchY[j], đẩy vào Queue phần tử matchY[j] ∈ X (Thăm liền bước) Input: file văn MATCH.INP • Dịng 1: chứa hai số m, n (m, n ≤ 100) theo thứ tự số X_đỉnh số Y_đỉnh cách dấu cách • Các dòng tiếp theo, dòng ghi hai số i, j cách dấu cách thể có cạnh nối hai đỉnh (X[i], Y[j]) Output: file văn MATCH.OUT, ghi ghép cực đại tìm 1 2 3 4 MATCH.INP 45 11 14 21 22 24 32 33 42 43 MATCH.OUT Match: 1) X[1] 2) X[2] 3) X[3] 4) X[4] - Y[1] Y[4] Y[3] Y[2] X Y P_4_11_1.PAS * Thuật tốn đường mở tìm ghép cực đại program MatchingProblem; const InputFile = 'MATCH.INP'; OutputFile = 'MATCH.OUT'; max = 100; var m, n: Integer; a: array[1 max, max] of Boolean; matchX, matchY: array[1 max] of Integer; Trace: array[1 max] of Integer; procedure Enter; var i, j: Integer; f: Text; begin Assign(f, InputFile); Reset(f); FillChar(a, SizeOf(a), False); ReadLn(f, m, n); while not SeekEof(f) begin ReadLn(f, i, j); a[i, j] := True; end; Close(f); end; procedure Init; {Khởi tạo ghép rỗng} begin Lê Minh Hoàng 270 Chuyên đề FillChar(matchX, SizeOf(matchX), 0); FillChar(matchY, SizeOf(matchY), 0); end; {Tìm đường mở, thấy trả Y_đỉnh chưa ghép đỉnh kết thúc đường mở, không thấy trả 0} function FindAugmentingPath: Integer; var Queue: array[1 max] of Integer; i, j, first, last: Integer; begin FillChar(Trace, SizeOf(Trace), 0); {Trace[j] = X_đỉnh liền trước Y[j] đường mở} last := 0; {Khởi tạo hàng đợi rỗng} for i := to m {Đẩy tất X_đỉnh chưa ghép vào hàng đợi} if matchX[i] = then begin Inc(last); Queue[last] := i; end; {Thuật tốn tìm kiếm theo chiều rộng} first := 1; while first Nếu cạnh trọng số lại tạo ghép k cạnh G ghép cần tìm Chứng minh: Theo giả thiết, cạnh G mang trọng số không âm nên ghép G có trọng số không âm, mà ghép mang trọng số 0, nên tất nhiên ghép đầy đủ trọng số nhỏ Lê Minh Hoàng 274 Chuyên đề Định lý 2: Với đỉnh Xi, ta cộng thêm số ∆(dương hay âm) vào tất cạnh liên thuộc với Xi (tương đương với việc cộng thêm ∆ vào tất phần tử thuộc hàng i ma trận C) khơng ảnh hưởng tới ghép đầy đủ trọng số nhỏ Chứng minh: Với ghép đầy đủ có cạnh ghép với X[i] Nên việc cộng thêm ∆ vào tất cạnh liên thuộc với X[i] làm tăng trọng số ghép lên ∆ Vì ban đầu, M ghép đầy đủ trọng số nhỏ sau thao tác trên, M ghép đầy đủ trọng số nhỏ Hệ quả: Với đỉnh Y[j], ta cộng thêm số ∆ (dương hay âm) vào tất cạnh liên thuộc với Y[j] (tương đương với việc cộng thêm ∆ vào tất phần tử thuộc cột j ma trận C) không ảnh hưởng tới ghép đầy đủ trọng số nhỏ Từ nhận tư tưởng thuật tốn: Từ đồ thị G, ta tìm chiến lược cộng / trừ cách hợp lý trọng số cạnh liên thuộc với đỉnh để đồ thị có cạnh trọng số không âm, mà cạnh trọng số đồ thị chứa ghép đầy đủ k cạnh Ví dụ: Biến đổi ma trận trọng số đồ thị hai phía đỉnh trái, đỉnh phải: -1 -1 ⎡0 0 ⎤ ⎢0 ⎥ ⎢ ⎥ ⎢0 ⎥ ⎣ ⎦ ⎡1 0 ⎤ ⎢0 ⎥ ⎥ ⎢ ⎢0 ⎥ ⎦ ⎣ X1 - Y3 X2 - Y2 X3 - Y1 +1 Hình 84: Phép xoay trọng số cạnh 12.3 THUẬT TOÁN 12.3.1 Các khái niệm: Để cho gọn, ta gọi cạnh trọng số G 0_cạnh Xét ghép M gồm 0_cạnh Những đỉnh ∈ M gọi đỉnh ghép, đỉnh lại gọi đỉnh chưa ghép Những 0_cạnh ∈ M gọi 0_cạnh ghép, 0_cạnh lại 0_cạnh chưa ghép Nếu ta định hướng lại 0_cạnh sau: Những 0_cạnh chưa ghép cho hướng từ tập X sang tập Y, 0_cạnh ghép cho hướng từ tập Y tập X Khi đó: Đường pha (Alternating Path) đường xuất phát từ X_đỉnh chưa ghép theo 0_cạnh định hướng Như dọc đường pha, 0_cạnh chưa ghép Đại học Sư phạm Hà Nội, 1999-2002 ... khác, chương trình việc in kết khơng phải thực lại thuật tốn Floyd Đại học Sư phạm Hà Nội, 199 9-2 002 Các thuật toán đồ thị 245 8. 8 NHẬN XÉT Bài toán đường dài đồ thị số trường hợp giải cách đổi... thấy rõ điều Bài tập Bài Giải thích đồ thị sau, cần tìm đường dài từ đỉnh tới đỉnh lại khơng thể dùng thuật tốn Dijkstra được, thử áp dụng thuật toán Dijkstra theo bước xem sao: Lê Minh Hoàng 246... chứng minh chi phí thời gian thực giải thuật trường hợp xấu O(n3) đồ thị dày O(n(n + m)logn) đồ thị thưa Tuy nhiên, giống thuật toán Ford-Fulkerson, thực tế phương pháp hoạt động nhanh Bài tập Bài

Ngày đăng: 13/08/2014, 20:22

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan