3 Một số ứng dụng của ghép cặp
3.1.2 Mô tả thuật toán giải
Định lý 3.1 là cơ sở cho thuật toán sau đây tìm ghép cặp có trọng số lớn nhất trong một đồ thị hai phần đầy đủ. Thuật toán bắt đầu từ một gán nhãn bất kỳ, sau đó lập đồ thị con ngang bằng và tìm một ghép cặp cực đại trong đồ thị con này.
Nếu ghép cặp tìm được là ghép cặp hoàn hảo thì theo Định lý 3.1, đó là ghép cặp có trọng số lớn nhất cần tìm. Ghép cặp đó là đầu ra (output) của thuật toán.
Nếu ghép cặp cực đại tìm được không phải là ghép cặp hoàn hảo thì cần đưa thêm một (hay một số) cạnh mới vào đồ thị ngang bằng và loại một số cạnh cũ khỏi đồ thị ngang bằng nhờ sửa lại nhãn của các đỉnh. Việc sửa nhãn (sẽ mô tả sau) phải làm sao cho các cạnh thuộc ghép cặp hiện có vẫn thuộc đồ thị ngang bằng.
Sau khi sửa lại đồ thị ngang bằng, thuật toán tìm cách mở rộng cây Hung- ga-ri (gồm xen kẽ cạnh tự do và cạnh ghép) để tăng số cạnh ghép (nếu tìm được đường cải tiến) hoặc kết nạp thêm đỉnh mới vào cây.
Trong trường hợp đầu, vòng lặp hiện tại kết thúc và thuật toán bắt đầu một vòng lặp mới với số cạnh ghép tăng lên. Trong trường hợp sau, một số đỉnh mới được thêm vào cây Hung-ga-ri. Cùng lắm sau nlần thêm đỉnh, cây này sẽ bao trùm hết mọi đỉnh của đồ thị ban đầu. Vì thế sau nhiều nhất n
lần, số cạnh ghép phải tăng lên. Thuật toán tiếp tục với ghép cặp mới cho tới khi tìm được ghép cặp hoàn hảo.
Bây giờ ta mô tả chi tiết quá trình sữa nhãn và sửa đổi đồ thị ngang bằng. Giả sửM là ghép cặp cực đại tìm được theo thuật toán. Cây Hung-ga-ri (gồm xen kẽ cạnh tự do và cạnh ghép) được phát triển từ tất cả các đỉnh tự do trong A. Các đỉnh thuộc A (kể cả đỉnh tự do) gặp trong quá trình tìm kiếm được đưa vào tậpS, còn các đỉnh thuộcB gặp trong quá trình tìm kiếm được đưa vào tập T. Ta có |S| > |T|. Đặt S = A\S, T = B\T. Ta thấy trong đồ thị ngang bằng không có cạnh đi từ S tới T và không có cạnh ghép đi từ T
tới S. Hình 3.2. minh hoạ cấu trúc các tập S và T. Các cạnh ghép được vẽ đậm, các cạnh còn lại đều thuộc G` (không vẽ các cạnh ∈ E(G)\E(G`)).
Hình 3.2: Các tập S và T lưu giữ trong thuật toán.
nhãn các đỉnh thuộcT cùng lượng d đó. Cách sửa này bảo đảm cho các cạnh đi từ S tới T và các cạnh ghép vẫn thuộc đồ thị ngang bằng. Các cạnh thuộc
G (nhưng không thuộc G`) đi từ S tới T là các ứng cử viên có thể chọn để đưa vào đồ thị ngang bằng, vì nhãn ở một đỉnh mút (∈ S) của cạnh bị giảm, còn nhãn của đỉnh kia (∈ T) không đổi. Thuật toán sẽ chọn d là số nhỏ nhất sao cho một (hay một số) cạnh nào đó ∈ E(G)\E(G`) được đưa vào đồ thị ngang bằng, cụ thể, chọn
d = min
i∈S,j∈T
dij > 0.
Giả sử cạnh được chọn đi từ đỉnh x ∈ S tới đỉnh y ∈ T. Nếu y là đỉnh tự do thì đã tìm được một đường cải tiến (đi từ gốc của cây tới y). Nếu trái lại (đỉnh y bị phủ bởi cạnh ghép) thì cây Hung-ga-ri được mở rộng bằng cách đưa y vào T và đưa đỉnh bị phủ kề y vào S. Loại khỏi đồ thị ngang bằng G`
tất cả các cạnh đi từ T tới S (vì nhãn của đỉnh ∈ T đã tăng lên d > 0, còn nhãn của đỉnh ∈ S không đổi).
Phân tích. Mỗi vòng lặp bao gồm các bước cần thực hiện để làm tăng số cạnh của ghép cặp. Có nhiều nhất O(n) vòng lặp (2n là số đỉnh của đồ thị G, số cạnh ghép 6 n), vì ở mỗi vòng lặp số cạnh ghép tăng thêm một. Trong mỗi vòng lặp ta tăng kích cỡ của cây Hung-ga-ri tối đa 6 n lần (cây
2n đỉnh có tối đa 2n−1 cạnh). Mỗi lần cần thời gian O(n2) để tìm ra cạnh từ S tới T đưa thêm vào đồ thị ngang bằng (duyệt tất cả các cạnh của đồ thị trong thời gian O(n2)). Vì thế, cách làm này cho một cận trên O(n4) đối với toàn bộ thời gian chạy của thuật toán.
Sau đây ta nêu tóm tắt các bước của thuật toán, rồi sau đó nêu cách thực thi thuật toán trong thời gian O(n3).
Thuật toán Kuhn-Munkres (còn gọi là phương pháp Hung-ga-ri). Bước 1. Lập đồ thị ngang bằng G` theo một cách gán nhãn ban đầu tuỳ ý.
Bước 2. Tìm ghép cặp cực đại trong G`.
Bước 3. Nếu đó là ghép cặp hoàn hảo thì dừng: ta nhận được lời giải. Bước 4. Sửa nhãn các đỉnh và sửa đổi đồ thị ngang bằng như đã nêu. Phát triển cây Hung-ga-ri từ mỗi đỉnh tự do thuộc A. Nếu có đường cải tiến thì tăng số cạnh của ghép cặp thêm một bằng cách đổi cạnh tự do (trên đường cải tiến) thành cạnh ghép và ngược lại, rồi tiếp tục phát triển cây Hung-ga-ri. Khi không còn đường cải tiến thì quay lại thực hiện bước 3.
Ở dạng bảng, thuật toán Kuhn-Munkres được cụ thể hoá như sau: Bước 1 (Lập đồ thị ngang bằng). Gán cho hàng i nhãn ui = max
16j6ncij
(i = 1, . . . , n), gán cho cột j nhãn vj = 0 (j = 1, . . . , n). Lập ma trận
D = [ui +vj −cij]n×n. Đồ thị ngang bằng tạo nên bởi các cạnh tương ứng với các phần tử 0 trong D (mỗi hàng có ít nhất một phần tử 0).
Bước 2 (Tìm ghép cặp cực đại). Với mỗi hàng, lần lượt từ hàng 1 tới hàng n, đánh dấu ∗ cho phần tử 0 đầu tiên trên hàng đó không nằm trên cột đã có 0∗ (phần tử 0 được đánh dấu ∗). Đặt S = ∅, T = ∅. Ta bắt đầu xây dựng cây Hung-ga-ri.
Lần lượt từ hàng 1 tới n, tìm hàng không chứa phần tử 0∗. Nếu không có hàng như thế thì chuyển sang bước 3. Giả sử đó là hàngi0, vì trên mỗi hàng đều có ít nhất một phần tử 0, nên trên hàng i0 phải có phần tử 0, chẳng hạn ở cột j0. Xuất phát từ ô (ik, jk) với k = 0, ta sẽ xây dựng một dây chuyền các ô kế tiếp nhau theo chiều dọc, ngang nối các phần tử 0 với 0∗ (thao tác A), 0∗ với 0 (thao tác B) như sau:
(A) Phát triển theo cột: Giả sử ta đang ở phần tử 0 trong ô (ik, jk) với
k > 0 và jk ∈/ T , tìm phần tử 0∗ trong cột jk. Nếu không tìm thấy phần tử 0∗ thì ta đã có một đường cải tiến (gồm xen kẽ các phần tử 0 và 0∗) và thực hiện thao tác (D) dưới đây. Nếu trái lại, giả sử tìm thấy phần tử 0∗ ở hàng ik+1, ta thêm ô (ik+1, jk) vào dây chuyền đang xét. Đưa ik, jk+1 vào S
và đưa jk vào T. Thực hiện thao tác (B) dưới đây.
(B) Phát triển theo hàng: Giả sử ta đang ở phần tử 0∗ trong ô (ik+1, jk)
với k > 0, Trên hàng ik+1 tìm phần tử 0không nằm trên cột đã có mặt trong dây chuyền đang xét (tìm trên cột ∈/ T). Nếu không có phần tử 0 như thế trên hàng này thì thực hiện thao tác (C) dưới đây. Nếu trái lại, giả sử có phần tử 0 ở cột jk+1, thêm ô (ik+1, jk+1) vào dây chuyền đang xét và đưa
jk+1 vào T. Tăng k lên 1 và lặp lại thao tác (A) trên.
(C) Tìm quay lui: Loại khỏi dây chuyền đang xét hai ô(ik, jk) và(ik+1, jk)
(chứa 0 và 0∗). Giảm k đi 1. Nếu còn ô trên dây chuyền (k > 0), ta lại xuất phát từ phần tử 0∗ trong ô (ik+1, jk) và lặp lại thao tác (B). Nếu không còn ô nào trên dây chuyền nữa (k = 0), ta tìm phần tử 0 ở hàng tiếp theo không chứa 0∗. Giả sử phần tử 0 tìm được ở hàng i00, cột j00. Nếu j00 ∈/ T thì đưa i00
vào S và chuyển tới tìm phần tử 0 ở hàng tiếp theo không chứa 0∗. Còn nếu
tử 0 trên hàng không chứa 0∗, ta chuyển sang bước 3.
(D) Tăng số cạnh ghép: Đổi mỗi phần tử 0 trên dây chuyền (đường cải tiến) nhận được thành 0∗ và đổi 0∗ thành 0 (như đổi cạnh tự do thành cạnh ghép và ngược lại), rồi đặt lại S = T = ∅. Nếu còn hàng chưa có phần tử
0∗ thì tìm phần tử 0 trên hàng đó và thực hiện thao tác (A) nêu trên, xuất phát từ phần tử 0mới này. Nếu không còn hàng nào như thế thì chuyển sang bước 3.
Bước 3 (Kiểm tra ghép cặp hoàn hảo). Nếu có đủ n phần tử 0∗ thì dừng: các phần tử 0∗ lập nên một ghép cặp hoàn hảo. Lời giải của bài toán là: người i được phân thực hiện công việc tương ứng với phần tử 0∗ trên hàng i.
Nếu số phần tử 0∗ nhỏ hơn n thì chuyển sang bước 4. Bước 4 (Sửa nhãn của đỉnh và sửa đồ thị ngang bằng). Tính số d = min i∈S,j /∈T dij > 0. Đổi ui ← ui −d(∀i ∈ S) và vj ← vj +d(∀j ∈ T). Các ui, vj khác không đổi. Sửa ma trậnD : dij ←dij−d(∀i ∈ S, j /∈ T);dij ← dij+d(∀i /∈ S, j ∈ T). Thêm vào đồ thị ngang bằng G` các ô (i, j) đạt dij = 0(∀i ∈ S, j /∈ T). Loại khỏi G` các ô (i, j) với mọi i /∈ S, j ∈ T. Quay lại thực hiện bước 2.