Chuyên đề tìm HIỂU bài TOÁN GHÉP cặp TRONG đồ THỊ HAI PHÍA

32 770 1
Chuyên đề tìm HIỂU bài TOÁN GHÉP cặp TRONG đồ THỊ HAI PHÍA

Đ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

TÌM HIỂU BÀI TOÁN GHÉP CẶP TRONG ĐỒ THỊ HAI PHÍA ******* I ĐẶT VẤN ĐỀ Giả sử có đồ thị yêu cầu tìm nhiều cạnh độc lập tốt Chúng ta phải để tìm chúng? Chúng ta có khả ghép cặp tất đỉnh theo cách không? Nếu không, làm chắn thực không thể? Có phần ngạc nhiên, toán sở không nằm trung tâm nhiều ứng dụng mà vấn đề thú vị lý thuyết đồ thị Một cặp ghép (matching) đồ thị G = (V,E) tập hợp cạnh G đôi đỉnh chung M cặp ghép U ⊆ V đỉnh U liên thuộc với cạnh M Các đỉnh U gọi đỉnh ghép (bởi M); Các đỉnh không liên thuộc với cạnh M gọi đỉnh chưa ghép Một đồ thị bao trùm quy bậc k gọi đồ thị k-nhân tử (k- factor) Như đồ thị H ⊆ G đồ thị 1-nhân tử (1-factor) G tập cạnh E(H) H cặp ghép V Ví dụ đồ thị Petersen chia thành đồ thị bao trùm quy bậc 1(đồ thị 1-nhân tử): đồ thị mà cạnh tô màu đỏ đồ thị bao trùm quy bậc (đồ thị 2-nhân tử): đồ thị mà cạnh tô màu xanh hình đây: k-regular graph(đồ thị quy bậc k) Tổng quát hóa toán ghép cặp tìm đồ thị G cho nhiều đồ thị rời tốt Các đồ thị đồ thị đẳng cấu với phần tử lớp đồ thị H Đây toán đóng gói Nó liên quan đến toán phủ toán hỏi đỉnh G thỏa mãn có mặt tất đồ thị đẳng cấu với đồ thị H: rõ ràng hơn, cần đỉnh để phủ số lớn k đồ thị lớp đồ thị H Nếu không phủ k đỉnh, có lẽ phủ f(k) đỉnh, f(k) phụ thuộc vào H không phụ thuộc vào G Chúng ta chứng minh H lớp chu trình tương ứng có hàm f xem xét toán đóng gói cạnh toán phủ cạnh: Có bao trùm độc lập mà tìm thấy đồ thị cho có phủ tất cạnh Trong mục 2,5 chứng minh định lý phủ đường cho đồ thị có hướng Giải thích định nghĩa: • spanning graph ( đồ thị bao trùm Đồ thị H đồ thị bao trùm đồ thị G tập đỉnh H trùng với tập đỉnh G Ta nói H bao trùm G) • k-factor ( đồ thị k-nhân tử) đồ thị bao trùm quy bậc k II BÀI TOÁN TÌM CẶP GHÉP CỰC ĐẠI TRÊN ĐỒ THỊ HAI PHÍA Đồ thị hai phía (Bipartite Graph) Các tên gọi đồ thị hai phía, đồ thị lưỡng phân, đồ thị phân đôi, đồ thị đối sánh hai phần v.v để chung 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ị Y X nối đỉnh X với đỉnh thuộc Y Khi người ta 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 gọi X_đỉnh, đỉnh thuộc Y gọi Y_đỉnh Để 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 toá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 có lẽ toán quan hệ hôn nhân trực quan Bài toán ghép cặp không trọng số đồ thị hai phía 2.1 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 cặp ghép (matching) G tập hợp cạnh G đôi đỉnh chung Bài toán ghép cặp (matching problem) tìm cặp ghép lớn (nghĩa có số cạnh lớn nhất) G Xét cặp 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 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 Một đường pha P kết thúc đỉnh chưa ghép Y gọi đường mở, sử dụng chuyển M thành cặp ghép lớn Đường mở đóng vai trò quan trọng việc tìm kiếm cặp ghép lớn Chúng ta sử dụng thuật toán đường mở để tìm cặp ghép lớn 2.2 Thuật toán đường mở - Bắt đầu từ cặp ghép M (thông thường cặp ghép khởi gán cặp ghép rỗng hay tìm thuật toán tham lam) - Sau tìm đường mở, tìm mở rộng cặp 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ở cặp ghép thời lớn for (∀x∈X) if then Ví dụ: tìm cặp ghép đồ thị hai phía sau: X1 Y1 X2 Y2 X3 Y3 X Y Như ví dụ trên, với cặp ghép hai cạnh M = {(X1, Y1), (X2, Y2)} đường mở tìm gồm cạnh: (X3, Y2) ∉ M (Y2, X2) ∈ M (X2, Y1) ∉ M (Y1, X1) ∈ M (X1, Y3) ∉ M Vậy ta loại cạnh (Y 2, X2) (Y1, X1) cặp ghép cũ thêm vào cạnh (X3, Y2), (X2, Y1), (X1, Y3) cặp ghép cạnh 2.3 Cài đặt a) 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] A[i, j] = FALSE cạnh nối đỉnh X[i] với đỉnh Y[j] b) Biểu diễn cặp ghép Để biểu diễn cặp 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 cặp 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 cặp ghép ta việc đặt matchX[i] := j matchY[j] := i; Để loại cạnh (X[i], Y[j]) khỏi cặp ghép ta việc đặt matchX[i] := matchY[j] := 0; c) Tìm đường mở nào? Vì đường mở X_đỉnh chưa ghép, theo cạnh chưa ghép sang tập Y, theo cạnh 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 toá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 Để tìm đường mở bắt đầu đỉnh x *∈X, ta khởi tạo hàng đợi (Queue) ban đầu có đỉnh x* Thuật toá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 Y_đỉnh chưa ghép đó, trình tìm kiếm dừng Còn ta thăm tới đỉnh y∈Y ghép, dựa vào kiện: từ y tới matchY[y] theo cạnh ghép định hướng ngược từ Y X, nên ta đánh dấu thăm y, thăm matchY[y], đẩy vào Queue phần tử matchY[y] ∈ X (Thăm liền bước) d) Nhập đồ thị từ file văn B_GRAPH.INP Dòng 1: Ghi 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]) 1 2 3 4 B_GRAPH.INP 1 2 2 3 4 OUTPUT Match: 1) X[1] 2) X[2] 3) X[3] 4) X[4] - Y[1] Y[4] Y[3] Y[2] Y X PROG11_1.PAS  Thuật toán đường mở tìm cặp ghép cực đại program MatchingProblem; const 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; {với y∈Y, Trace[y] đỉnh ∈X liền trước đỉnh y đường mở} procedure Enter; var f: Text; i, j: Integer; begin FillChar(a, SizeOf(a), False); Assign(f, 'B_GRAPH.INP'); Reset(f); Readln(f, m, n); while not SeekEof(f) begin Readln(f, i, j); a[i, j] := True; end; end; procedure Init; begin FillChar(matchX, SizeOf(matchX), 0); FillChar(matchY, SizeOf(matchY), 0); end; {Tìm đường mở bắt đầu XStart, thấy trả đỉnh kết thúc đường mở, không thấy trả 0} function FindAugmentingPath(xStart: Integer): Integer; var Queue: array[1 max] of Integer; x, y, first, last: Integer; begin FillChar(Trace, SizeOf(Trace), 0); Queue[1] := xStart; first := 1; last := 1; {Khởi tạo Queue gồm đỉnh xuất phát} repeat x := Queue[first]; Inc(first); {Lấy x khỏi Queue} for y := to n {Xét Y_đỉnh, lọc Y_đỉnh chưa thăm kề với x qua cạnh chưa ghép} if (Trace[y] = 0) and a[x, y] and (matchX[x] y) then begin Trace[y] := x; if matchY[y] = then {Nếu y chưa ghép} begin FindAugmentingPath := y; {Xác định đường mở kết thúc y thoát luôn} Exit; end; Inc(last); Queue[last] := matchY[y]; {Đẩy matchY[y] vào Queue} end; until first > last; {Hàng đợi rỗng} FindAugmentingPath := 0; {Ở không Exit tức đường mở} end; procedure Enlarge(f: Integer); {Nới rộng cặp ghép đường mở kết thúc f} var x f x, next: Integer; begin x f next next repeat x := Trace[f]; next := matchX[x]; matchX[x] := f; matchY[f] := x; f := next; until f = 0; end; procedure Solve; {Thuật toán đường mở} var x, y: Integer; begin for x := to m begin y := FindAugmentingPath(x); if y then Enlarge(y); end; end; procedure PrintResult; var i, Count: Integer; begin Writeln('Match: '); Count := 0; for i := to m if matchX[i] then begin Inc(Count); Writeln(Count, ') X[', i, '] - Y[', matchX[i], ']'); end; end; begin Enter; Init; 10 Cộng tất trọng số cạnh liên thuộc với Y3 lên X1 Y1 x* = X4 X1 Y1 X2 Y2 Vẫn không tìm đường X2 Y2 2=∆ mở: Tập X_đỉnh đến X3 Y3 từ X4 đường X3 Y3 pha: X4 Y4 {X1, X2, X3, X4} Tập Y_đỉnh đến X4 Y4 từ X4 đường pha: {Y1, Y2, Y3} Giá trị xoay ∆ = (Cạnh X2-Y4) Trừ tất trọng số cạnh liên thuộc với {X1, X2, X3, X4} Cộng tất trọng số cạnh liên thuộc với {Y1, Y2, Y3} lên 18 X1 Y1 X1 Y1 X2 Y2 X3 Y3 x* = X4 X2 Y2 Tìm đường mở: X3 Y3 X4 → Y3 → X3 → Y → X X4 Y4 →Y1 → X2 → Y4 X4 Y4 Tăng cặp Xong Để ý không tìm thấy đường mở xuất phát x * trình tìm kiếm đồ thị cho ta pha gốc x * Giá trị xoay ∆ thực chất trọng số nhỏ cạnh nối X_đỉnh pha với Y_đỉnh pha (cạnh ngoài) Việc trừ ∆ vào cạnh liên thuộc với X_đỉnh pha cộng ∆ vào cạnh liên thuộc với Y_đỉnh pha làm cho cạnh nói trở thành 0_cạnh, cạnh khác có trọng số ≥ Nhưng quan trọng tất cạnh pha 0_cạnh Điều đảm bảo cho trình tìm kiếm đồ thị lần sau xây dựng pha lớn pha cũ (Thể chỗ: tập VisitedY rộng trước phần tử) Vì tập Y_ đỉnh ghép hữu hạn nên sau không k bước, có Y_đỉnh chưa ghép ∈ VisitedY, tức tìm đường mở Trên thực tế, để chương trình hoạt động nhanh hơn, bước khởi tạo, người ta thêm thao tác: Với đỉnh x ∈ X, xác định trọng số nhỏ cạnh liên thuộc với x, sau trừ tất trọng số cạnh liên thuộc với x trọng số nhỏ Làm tương tự với Y_đỉnh Điều tương đương với việc trừ tất phần tử hàng ma trận C giá trị nhỏ hàng đó, lại trừ tất phần tử cột ma trận C phần tử nhỏ cột Khi 19 số 0_cạnh đồ thị nhiều, chứa cặp ghép đầy đủ cần qua bước biến đổi chứa cặp ghép đầy đủ k cạnh Để tưởng nhớ hai nhà toán học König Egervary, người đặt sở lý thuyết cho phương pháp, người ta lấy tên đất nước sinh hai nhà toán học để đặt tên cho thuật toán Mặc dù sau có số cải tiến tên gọi Thuật toán Hungari (Hungarian Algorithm) dùng phổ biến 3.4 Cài đặt 3.4.1 Phương pháp đối ngẫu Kuhn-Munkres (Không làm biến đổi ma trận C ban đầu) Phương pháp Kuhn-Munkres tìm hai dãy số Fx[1 k] Fy[1 k] thoả mãn: + c[i, j] - Fx[i] - Fy[j] ≥ + Tập cạnh (X[i], Y[j]) thoả mãn c[i, j] - Fx[i] - Fy[j] = chứa trọn cặp ghép đầy đủ k cạnh, cặp ghép cần tìm Chứng minh: Nếu tìm hai dãy số thoả mãn ta việc thực hai thao tác: Với đỉnh X[i], trừ tất trọng số cạnh liên thuộc với X[i] Fx[i] Với đỉnh Y[j], trừ tất trọng số cạnh liên thuộc với Y[j] Fy[j] (Hai thao tác tương đương với việc trừ tất trọng số cạnh (X[i], Y[j]) lượng Fx[i] + Fy[j] tức c[i, j] := c[i, j] - Fx[i] - Fy[j]) Thì dễ thấy đồ thị tạo thành gồm có cạnh trọng số không âm 0_cạnh đồ thị chứa trọn cặp ghép đầy đủ 0 M M Fx[1] = 2 M M Fx[2] = M M Fx[3] = 20 M M Fy[1] = Fy[2] = Fy[3] = Fy[4] = -2 -2 -3 Fx[4] = (Có nhiều phương án khác: Fx = (0, 0, 1, 1); Fy = (0, 0, -1, 2) đúng) Vậy phương pháp Kuhn-Munkres đưa việc biến đổi đồ thị G (biến đổi ma trận C) việc biến đổi hay dãy số Fx Fy Việc trừ ∆ vào trọng số tất cạnh liên thuộc với X[i] tương đương với việc tăng Fx[i] lên ∆ Việc cộng ∆ vào trọng số tất cạnh liên thuộc với Y[j] tương đương với giảm Fy[j] ∆ Khi cần biết trọng số cạnh (X[i], Y[j]) sau bước biến đổi, thay viết c[i, j], ta viết c[i, j] - Fx[i] - Fy[j] Ví dụ: Thủ tục tìm đường mở thuật toán Hungari đòi hỏi phải xác định cạnh 0_cạnh, cài đặt phương pháp Kuhn-Munkres, việc xác định cạnh 0_cạnh kiểm tra đẳng thức: c[i, j] - Fx[i] - Fy[j] = hay c[i, j] = Fx[i] + Fy[j] Sơ đồ cài đặt phương pháp Kuhn-Munkres viết sau: Bước Khởi tạo: M := ∅; Việc khởi tạo Fx, Fy có nhiều cách chẳng hạn Fx[i] := 0; Fy[j] := với ∀i, j Hoặc: (c[i, j]) với ∀i Sau đặt Fy[j] := (c[i, j] − Fx[i]) với ∀j Fx[i] := 1≤ j≤ k 1≤i ≤ k (Miễn c[i, j] - Fx[i] - Fy[j] ≥ 0) Bước Với đỉnh x*∈X, ta tìm cách ghép x* sau: Bắt đầu từ đỉnh x*, thử tìm đường mở bắt đầu x* thuật toán tìm kiếm đồ thị (BFS DFS) Lưu ý 0_cạnh cạnh thoả mãn c[i, j] = Fx[i] + Fy[j] Có hai khả xảy ra: - Hoặc tìm đường mở dọc theo đường mở, ta loại bỏ cạnh ghép khỏi M thêm vào M cạnh chưa ghép 21 - Hoặc không tìm đường mở xác định hai tập: + VisitedX = {Tập X_đỉnh đến từ x* đường pha} + VisitedY = {Tập Y_đỉnh đến từ x* đường pha} + Đặt ∆:=min{c[i, j] - Fx[i] - Fy[j] ∀X[i]∈VisitedX; ∀Y[j] ∉ VisitedY} + Với ∀X[i] ∈ VisitedX: Fx[i] := Fx[i] + ∆; + Với ∀Y[j] ∈ VisitedY: Fy[j] := Fy[j] - ∆; + Lặp lại thủ tục tìm đường mở xuất phát x * tìm đường mở Lưu ý phương pháp Kuhn-Munkres không làm thay đổi ma trận C ban đầu Điều thực hữu ích trường hợp trọng số cạnh (X[i], Y[j]) không cho cách tường minh giá trị C[i, j] mà lại cho hàm c(i, j) Trong trường hợp này, việc trừ hàng/cộng cột trực tiếp ma trận chi phí C thực 3.4.2 Dưới ta cài đặt chương trình giải toán phân công thuật toán Hungari với phương pháp đối ngẫu Kuhn-Munkres: a) Biểu diễn cặp ghép Để biểu diễn cặp ghép, ta sử dụng hai mảng: matchX[1 k] matchY[1 k] + 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 cặp 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] = 22 + Để thêm cạnh (X[i], Y[j]) vào cặp ghép việc đặt matchX[i] := j matchY[j] := i; + Để loại cạnh (X[i], Y[j]) khỏi cặp ghép việc đặt matchX[i] := matchY[j] := 0; b) Tìm đường mở nào? Ta tìm đường mở xây dựng hai tập VisitedX VisitedY thuật toán tìm kiếm theo chiều rộng xét tới đỉnh 0_cạnh định hướng nói phần đầu: Khởi tạo hàng đợi (Queue) ban đầu có đỉnh x * Thuật toá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 Y_đỉnh chưa ghép đó, trình tìm kiếm dừng Còn ta thăm tới đỉnh y ∈ Y ghép, dựa vào kiện: từ y tới matchY[y] theo 0_cạnh định hướng, nên ta đánh dấu thăm y, thăm matchY[y], đẩy vào Queue phần tử matchY[y] ∈ X 3.4.3 Nhập liệu từ file văn ASSIGN.INP - Dòng 1: Ghi hai số m, n theo thứ tự số thợ số việc cách dấu cách (m, n ≤ 100) - Các dòng tiếp theo, dòng ghi ba số i, j, c[i, j] cách dấu cách thể thợ i làm việc j chi phí để làm c[i, j] (1 ≤ i ≤ m; ≤ j ≤ n; ≤ c[i, j] ≤ 100) 23 X 1 2 3 4 19 Y ASSIGN.INP 1 2 3 4 19 OUTPUT Optimal assignment: 1) X[1] - Y[1] 2) X[2] - Y[4] 3) X[3] - Y[2] 4) X[4] - Y[3] Cost: PROG12_1.PAS  Thuật toán Hungari program AssignmentProblemSolve; {Giải toán phân công} const max = 100; maxC = 10001; var c: array[1 max, max] of Integer; Fx, Fy, matchX, matchY, Trace: array[1 max] of Integer; VisitedX, VisitedY: array[1 max] of Boolean; m, n, k: Integer; procedure Enter; var f: Text; i, j: Integer; begin Assign(f, 'ASSIGN.INP'); Reset(f); Readln(f, m, n); if m > n then k := m else k := n; {k := max(m, n), coi có k thợ, k việc} 24 for i := to k for j := to k c[i, j] := maxC; { (i, j) file c[i, j] := maxC} while not SeekEof(f) Readln(f, i, j, c[i, j]); Close(f); end; procedure Init; var i, j: Integer; begin FillChar(matchX, SizeOf(matchX), 0); FillChar(matchY, SizeOf(matchY), 0); {Khởi tạo cặp ghép ∅} {Ta hoàn toàn đặt Fx Fy 0, để hiệu nên: } for i := to k {Trừ tất trọng số cạnh liên thuộc với X[i] trọng số cạnh nhỏ nhất} begin {⇔ Đặt Fx[i] := Trọng số cạnh nhỏ liên thuộc với X[i]} Fx[i] := maxC; for j := to k if c[i, j] < Fx[i] then Fx[i] := c[i, j]; end; for j := to k {Rồi trừ tất trọng số cạnh liên thuộc với Y[j] cho trọng số cạnh nhỏ nhất} begin {Lưu ý trọng số cạnh (X[i], Y[j]) c[i, j] - Fx[i] không c[i, j] nữa} Fy[j] := maxC; for i := to k if c[i, j] - Fx[i] < Fy[j] then Fy[j] := c[i, j] - Fx[i]; 25 end; end; {Tìm đường mở xuất phát từ StartX ∈ X, tìm thấy trả Y_đỉnh kết thúc đường mở, không thấy trả 0} function FindAugmentingPath(StartX: Integer): Integer; var Queue: array[1 max] of Integer; x, y, first, last: Integer; begin FillChar(VisitedX, SizeOf(VisitedX), False); FillChar(VisitedY, SizeOf(VisitedY), False); Queue[1] := StartX; first := 1; last := 1; VisitedX[StartX] := True; {Duy StartX thăm đẩy vào Queue} repeat x := Queue[first]; Inc(first); {Lấy đỉnh x∈X khỏi Queue} for y := to k {Xét Y_đỉnh chưa thăm, kề với x qua 0_cạnh chưa ghép} if not VisitedY[y] and (matchX[x] y) and (c[x, y] = Fx[x] + Fy[y]) then begin Trace[y] := x; {Lưu vết đường đi: đỉnh liền trước y đường StartX tới y x} if matchY[y] = then {nếu y chưa ghép tức tìm thấy đường mở từ StartX tới y} begin FindAugmentingPath := y; Exit; {Ghi nhận thoát ngay} 26 end; VisitedY[y] := True; {Nếu y ghép đánh dấu thăm y} VisitedX[matchY[y]] := True; {Thăm matchY[y]} Inc(last); Queue[last] := matchY[y]; {Đẩy matchY[y] vào hàng đợi} end; until first > last; {Cho tới hàng đợi rỗng, trình BFS xong} FindAugmentingPath := 0; {ở không Exit tức đường mở từ StartX} end; procedure SubX_AddY; {Xoay đồ thị G} var x, y, Delta: Integer; begin Delta := maxC; {Trước hết tính Delta := trọng số nhỏ cạnh nối VisitedX với Y\VisitedY} for x := to k if VisitedX[x] then for y := to k if not VisitedY[y] and (c[x, y] - Fx[x] - Fy[y] < Delta) then Delta := c[x, y] - Fx[x] - Fy[y]; for x := to k {Trừ tất trọng số cạnh liên thuộc với VisitedX Delta} if VisitedX[x] then Fx[x] := Fx[x] + Delta; for y := to k {Cộng tất trọng số cạnh liên thuộc với VisitedY lên Delta} if VisitedY[y] then Fy[y] := Fy[y] - Delta; end; 27 {Tăng cặp dựa đường mở kết thúc f∈Y} procedure Enlarge(f: Integer); var x, next: Integer; x f begin x f next repeat next x := Trace[f]; next := matchX[x]; matchX[x] := f; matchY[f] := x; f := Next; until f = 0; end; procedure Solve; {Thuật toán Hungari} var x, y: Integer; begin for x := to k {Xét ∀x∈X} begin repeat y := FindAugmentingPath(x); {Tìm đường mở xuất phát x} if y = then SubX_AddY; {Nếu không tìm thấy xoay đồ thị} until y 0; {Cho tới tìm thấy đường mở} Enlarge(y); {Nới rộng cặp ghép đường mở tìm được, x y trở thành ghép} end; end; 28 procedure Result; var x, y, Count, W: Integer; begin Writeln('Optimal assignment:'); W := 0; Count := 0; for x := to k begin y := matchX[x]; {Những cạnh có trọng số maxC tương ứng với thợ không giao việc việc không phân công} if c[x, y] < maxC then {Nên cần quan tâm tới phép phân công thật sự} begin Inc(Count); Writeln(Count:5, ') X[', x, '] - Y[', y, '] ', c[x, y]); W := W + c[x, y]; end; end; Writeln('Cost: ', W); end; begin Enter; Init; Solve; Result; 29 end Nhận xét: Nếu cài đặt cho dù đồ thị có cạnh mang trọng số âm, chương trình tìm cặp ghép cực đại với trọng số cực tiểu Lý do: Ban đầu, ta trừ tất phần tử hàng ma trận C giá trị nhỏ hàng đó, lại trừ tất phần tử cột ma trận C giá trị nhỏ cột (Phép trừ làm gián tiếp qua Fx, Fy trừ trực tiếp ma trận C) Nên sau bước này, tất cạnh đồ thị có trọng số không âm phần tử nhỏ cột C chắn Sau kết thúc thuật toán, tổng tất phần tử hai dãy Fx, Fy trọng số cực tiểu cặp ghép đầy đủ tìm đồ thị ban đầu Lại tương tự toán đường ngắn nhất, ta thêm vào đồ thị số đỉnh giả số cạnh để đồ thị hai phía đầy đủ giải toán đồ thị Những cạnh giả thêm vào gán trọng số đủ lớn Giá trị đủ lớn phải chọn nào?, phải lớn trọng số tất cặp ghép tạo thành từ cạnh thật Khi giá trị lớn dẫn đến nhiều phiền toái tổ chức liệu Vậy ta nên có giải pháp tốt cho vấn đề này, chẳng hạn giả thiết cho trọng số không âm nên ta lấy giá trị đặc biệt -1 gán cho cạnh giả, bước phải xét cạnh, ta thêm vào phép kiểm tra để xét cạnh trọng số khác -1 Khi tìm đường mở xuất phát từ x, không tìm thấy ta phải tìm giá trị xoay ∆ trọng số nhỏ cạnh nối X_đỉnh pha với Y_đỉnh pha, cạnh nối (hay nói tất cạnh nối X_đỉnh pha với Y_đỉnh pha cạnh trọng số -1) ta khẳng định đỉnh x ghép bỏ qua để tìm cách ghép X_đỉnh khác Một vấn đề phải cẩn thận việc ước lượng độ lớn phần tử Fx Fy Nếu giả thiết cho trọng số không 500 ta 30 dựa vào bất đẳng thức Fx(x) + Fy(y) ≤ c(x, y) mà khẳng định phần tử Fx Fy ≤ 500 Hãy tự tìm ví dụ để hiểu rõ chất thuật toán Bài toán tìm cặp ghép cực đại với trọng số cực đại đồ thị hai phía Bài toán tìm cặp ghép cực đại với trọng số cực đại giải nhờ phương pháp Hungari cách đổi dấu tất phần tử ma trận chi phí (Nhờ nhận xét 1) Khi cài đặt, ta sửa lại đôi chút chương trình để giải toán tìm cặp ghép cực đại với trọng số cực đại mà không cần đổi dấu trọng số Cụ thể sau: Bước Khởi tạo: M := ∅; Khởi tạo hai dãy Fx Fy thoả mãn: ∀i, j: Fx[i] + Fy[j] ≥ c[i, j]; Chẳng hạn ta đặt Fx[i] := Phần tử lớn dòng i ma trận C đặt Fy[j] := Bước Với đỉnh x*∈X, ta tìm cách ghép x* sau: Với cách hiểu 0_cạnh cạnh thoả mãn c[i, j] = Fx[i] + Fy[j] Bắt đầu từ đỉnh x*, thử tìm đường mở bắt đầu x* Có hai khả xảy ra: - Hoặc tìm đường mở dọc theo đường mở, ta loại bỏ cạnh ghép khỏi M thêm vào M cạnh chưa ghép - Hoặc không tìm đường mở xác định hai tập: + VisitedX = {Tập X_đỉnh đến từ x* đường pha} + VisitedY = {Tập Y_đỉnh đến từ x* đường pha} + Đặt ∆:= min{Fx[i] + Fy[j] - c[i, j]  ∀X[i] ∈ VisitedX; ∀Y[j] ∉ VisitedY} + Với ∀X[i] ∈ VisitedX: Fx[i] := Fx[i] - ∆; + Với ∀Y[j] ∈ VisitedY: Fy[j] := Fy[j] + ∆; 31 + Lặp lại thủ tục tìm đường mở xuất phát x* tìm đường mở Bước Sau bước X_đỉnh ghép, ta cặp ghép đầy đủ k cạnh với trọng số lớn nhất: Dễ dàng chứng minh tính đắn phương pháp, ta đặt: c'[i, j] = - c[i, j]; F'x[i] := - Fx[i]; F'y[j] = - Fy[j] Thì toán trở thành tìm cặp ghép đầy đủ trọng số cực tiểu đồ thị hai phía với ma trận trọng số c'[1 k, k] Bài toán giải cách tính hai dãy đối ngẫu F'x F'y Từ biến đổi đại số bản, ta kiểm chứng tính tương đương bước phương pháp nêu với bước phương pháp Kuhn-Munkres mục trước III KẾT LUẬN Trên số nghiên cứu mà vận dụng trình giảng dạy lý thuyết đồ thị Thông qua số tập cặp ghép đồ thị hai phía trên, học sinh rèn luyện cách xây dựng vận dụng cấu trúc liệu Quee để giải toán đồ thị Để đề tài hoàn thiện hơn, việc sử dụng đạt hiệu cao hơn, mong thầy cô đồng nghiệp đóng góp ý kiến để việc giảng dạy nhà trường ngày hiệu hơn, giúp em học sinh học tốt IV TÀI LIỆU THAM KHẢO [1] Reinhanrd Diestel - GRAPH THEORY, Electronic Edition 2000, 2005 [2] R Sedgevick – Algorithms, Addison- Wesley, 1990 Bản dịch tiếng Việt: Cẩm nang thuật toán (tập 1,2) Địa trang web tham khảo http://vi.wikipedia.org/wiki http://www.eli.sdsu.edu/ http://sage.mc.yu.edu http://www.cs.auckland.ac.nz/software/AlgAnim 32 [...]... cạnh của đồ thị sẽ có trọng số không âm bởi phần tử nhỏ nhất trên mỗi cột của C chắc chắn là 0 2 Sau khi kết thúc thuật toán, tổng tất cả các phần tử ở hai dãy Fx, Fy bằng trọng số cực tiểu của cặp ghép đầy đủ tìm được trên đồ thị ban đầu 3 Lại tương tự như bài toán đường đi ngắn nhất, ta thêm vào đồ thị một số đỉnh giả và một số cạnh để được đồ thị hai phía đầy đủ và giải bài toán trên đồ thị đó Những... này trong trường hợp xấu nhất sẽ là O(n3) đối với đồ thị dày và O(n(n + m)logn) đối với đồ thị thưa Tuy nhiên, cũng giống như thuật toán Ford-Fulkerson, trên thực tế phương pháp này hoạt động rất nhanh 3 Bài toán tìm cặp ghép cực đại với trọng số cực tiểu trên đồ thị hai phía – thuật toán Hungari 3.1 Bài toán phân công 11 - Đây là một dạng bài toán trong thực tế thường hay gặp Phát biểu như sau: Có... phải hết sức cẩn thận trong việc ước lượng độ lớn của các phần tử Fx và Fy Nếu như giả thiết cho các trọng số không quá 500 thì ta không thể 30 dựa vào bất đẳng thức Fx(x) + Fy(y) ≤ c(x, y) mà khẳng định các phần tử trong Fx và Fy cũng ≤ 500 Hãy tự tìm ví dụ để hiểu rõ hơn bản chất thuật toán 4 Bài toán tìm cặp ghép cực đại với trọng số cực đại trên đồ thị hai phía Bài toán tìm cặp ghép cực đại với trọng... ảnh hưởng tới cặp ghép đầy đủ trọng số nhỏ nhất Từ đây có thể nhận ra tư tưởng của thuật toán: Từ đồ thị G, ta tìm chiến lược cộng/trừ một cách hợp lý trọng số của các cạnh liên thuộc với một đỉnh nào đó để được một đồ thị mới vẫn có các cạnh trọng số không âm, mà các cạnh trọng số 0 của đồ thị mới đó chứa một cặp ghép đầy đủ k cạnh 13 Ví dụ: Biến đổi ma trận trọng số của đồ thị hai phía 3 đỉnh trái,... mọi phép phân công có thể Với mỗi cặp đỉnh (u, v): u ∈ X và v ∈ Y Nếu (u, v) ∉ E thì ta bổ sung cạnh (u, v) vào E với trọng số là M - Khi đó ta được G là một đồ thị hai phía đầy đủ (Đồ thị hai phía mà giữa một đỉnh bất kỳ của X và một đỉnh bất kỳ của Y đều có cạnh nối) Và nếu như ta tìm được cặp ghép đầy đủ k cạnh mang trọng số nhỏ nhất thì ta chỉ cần loại bỏ khỏi cặp ghép đó những cạnh mang trọng số... 3.3.2 Thuật toán Hungari Bước 1 Khởi tạo: + Một cặp ghép M := ∅ Bước 2 Với mọi đỉnh x*∈X, ta tìm cách ghép x* như sau: + Bắt đầu từ đỉnh x* chưa ghép, thử tìm đường mở bắt đầu ở x* bằng thuật toán tìm kiếm trên đồ thị (BFS hoặc DFS - thông thường nên dùng BFS để tìm đường qua ít cạnh nhất) có hai khả năng xảy ra: + Hoặc tìm được đường mở thì dọc theo đường mở, ta loại bỏ những cạnh đã ghép khỏi M và... Lặp lại thủ tục tìm đường mở xuất phát tại x* cho tới khi tìm ra đường mở Bước 3 Sau bước 2 thì mọi X_đỉnh đều đã ghép, ta được một cặp ghép đầy đủ k cạnh với trọng số lớn nhất: Dễ dàng chứng minh được tính đúng đắn của phương pháp, bởi nếu ta đặt: c'[i, j] = - c[i, j]; F'x[i] := - Fx[i]; F'y[j] = - Fy[j] Thì bài toán trở thành tìm cặp ghép đầy đủ trọng số cực tiểu trên đồ thị hai phía với ma trận... giải bài toán phân công bằng thuật toán Hungari với phương pháp đối ngẫu Kuhn-Munkres: a) Biểu diễn cặp ghép Để biểu diễn cặp ghép, ta sử dụng hai mảng: matchX[1 k] và matchY[1 k] + matchX[i] là đỉnh thuộc tập Y ghép với đỉnh X[i] + matchY[j] là đỉnh thuộc tập X ghép với đỉnh Y[j] Tức là nếu như cạnh (X[i], Y[j]) thuộc cặp ghép thì matchX[i] = j và matchY[j] = i Quy ước rằng: + Nếu như X[i] chưa ghép. .. i, j - Ra: Cặp ghép đầy đủ trọng số nhỏ nhất 12 Hai định lý sau đây tuy rất đơn giản nhưng là những định lý quan trọng tạo cơ sở cho thuật toán sẽ trình bày: Định lý 1: Loại bỏ khỏi G những cạnh trọng số > 0 Nếu những cạnh có trọng số 0 còn lại tạo ra cặp ghép k cạnh trong G thì đây là cặp ghép cần tìm Chứng minh: Theo giả thiết, các cạnh của G mang trọng số không âm nên bất kỳ cặp ghép nào trong G cũng... này vô lý • Biến đổi đồ thị G như sau: Với ∀x ∈ VisitedX, trừ ∆ vào trọng số những cạnh liên thuộc với x, Với ∀ y ∈ VisitedY, cộng ∆ vào trọng số những cạnh liên thuộc với y • Lặp lại thủ tục tìm kiếm trên đồ thị thử tìm đường mở xuất phát ở x * cho tới khi tìm ra đường mở Bước 3 Sau bước 2 thì mọi X_đỉnh đều được ghép, in kết quả về cặp ghép tìm được: 15 Mô hình cài đặt của thuật toán có thể viết như ... CỰC ĐẠI TRÊN ĐỒ THỊ HAI PHÍA Đồ thị hai phía (Bipartite Graph) Các tên gọi đồ thị hai phía, đồ thị lưỡng phân, đồ thị phân đôi, đồ thị đối sánh hai phần v.v để chung dạng đơn đồ thị vô hướng... graph ( đồ thị bao trùm Đồ thị H đồ thị bao trùm đồ thị G tập đỉnh H trùng với tập đỉnh G Ta nói H bao trùm G) • k-factor ( đồ thị k-nhân tử) đồ thị bao trùm quy bậc k II BÀI TOÁN TÌM CẶP GHÉP CỰC... khẳng định phần tử Fx Fy ≤ 500 Hãy tự tìm ví dụ để hiểu rõ chất thuật toán Bài toán tìm cặp ghép cực đại với trọng số cực đại đồ thị hai phía Bài toán tìm cặp ghép cực đại với trọng số cực đại giải

Ngày đăng: 19/01/2016, 19:05

Từ khóa liên quan

Mục lục

  • 1. Đồ thị hai phía (Bipartite Graph)

    • c) Tìm đường mở như thế nào?

    • d) Nhập đồ thị từ file văn bản B_GRAPH.INP

    • 3.4.2. Dưới đây ta sẽ cài đặt chương trình giải bài toán phân công bằng thuật toán Hungari với phương pháp đối ngẫu Kuhn-Munkres:

    • 3.4.3. Nhập dữ liệu từ file văn bản ASSIGN.INP

    • 4. Bài toán tìm cặp ghép cực đại với trọng số cực đại trên đồ thị hai phía

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

Tài liệu liên quan