1. 程式人生 > 其它 >【演算法學習筆記】26:匈牙利演算法(二分圖最大匹配)

【演算法學習筆記】26:匈牙利演算法(二分圖最大匹配)

技術標籤:演算法(學習)匈牙利演算法二分圖二分圖匹配圖論

1 簡述

給定一個二分圖,例如:
在這裡插入圖片描述

匈牙利演算法能夠快速的計算出一種匹配方式,使得匹配的數量最多。注意,一個成功的匹配方式中,沒有兩條邊是共用了同一個點的。

形象的說,這個問題可以理解成二分圖兩邊分別是男生和女生,有連線的表示可以湊成一對,匈牙利演算法就是用來計算最多能夠湊成多少對(不存在腳踏多條船的情況)。


例如左邊是男生,右邊是女生,可以任選一方為主動方,比如是男生方,那麼流程如下。

對於每個男生結點,對所有與之有連線的女生結點,檢查對應的女生是不是單身,如果是就直接湊成一對。那麼在上圖的例子中,前兩個男生都可以直接匹配到自己連線的第一個女生:

在這裡插入圖片描述

對於男生 3 3 3而言,他所能匹配的第一個女生是 2 2 2,但是這個女生已經是非單身了。這個時候就要去不斷嘗試,嘗試讓女生 2 2 2已經匹配的男生 1 1 1換一個匹配的女生(而不會讓當前已經成對的匹配數量減少)。

接下來男生 1 1 1檢查自己所能匹配的下一個女生 4 4 4,這個女生是非單身,所以將男 1 1 1與女 4 4 4匹配,此時女 2 2 2被釋放出來,得以和男 3 3 3匹配:
在這裡插入圖片描述

接下來檢查下一個男生 4 4 4,它所能匹配的女生 3 3 3是單身,將他倆匹配:
在這裡插入圖片描述

至此,能匹配的都匹配上了,這個二分圖的最大匹配數量是4。

2 模板題:二分圖的最大匹配

求最大匹配的時候,可以直接存到鄰接表裡,因為遍歷的時候是對每個男生遍歷所有能匹配的女生,所以只要存一下從男生到女生的邊,不需要像存普通無向圖那樣存雙向邊。

在實現時要注意,int match[]陣列用來記錄每個女生當前匹配的男生是哪一個(存標號),如果單身裡面存的就是0

由於在匈牙利演算法中,即使一個女生已經有匹配了,也可能被更換匹配,所以還需要每次清空一個bool st[]陣列,來記錄當前給某個男生找匹配的過程中,每個女生有沒有遍歷過,防止出現死迴圈。

#include <iostream>
#include <cstring>

using namespace std;

// 由於只存單項,邊M不用開兩倍
const int N = 510, M = 1e5 + 10;

// 鄰接表
int h[N], e[M], ne[M]
, idx; void add_edge(int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx ++ ; } // match記錄女生當前匹配的男生 int match[N]; // st記錄當前這輪每個女生有沒有遍歷過 // st[j] = true時候,j這個女生是被禁用的 bool st[N]; // 匈牙利演算法,嘗試給x找一個女朋友 bool find(int x) { // 對於能夠和x匹配的所有女生j for (int i = h[x]; i != -1; i = ne[i]) { int j = e[i]; // 如果j沒有被禁用(沒有遍歷過) if (!st[j]) { // 就將其鎖定,因為要嘗試讓x匹配j st[j] = true; // 如果j本來就是單身,或者j的男朋友能換一個女朋友 if (match[j] == 0 || find(match[j])) { // 就將x匹配上j match[j] = x; // 因為x能找到女朋友,所以返回true return true; } } } // 嘗試了x的所有能匹配的j都不行,就返回false return false; } int main() { // 讀取二分圖a->b memset(h, -1, sizeof h); int n1, n2, m; cin >> n1 >> n2 >> m; for (int i = 0; i < m; i ++ ) { int a, b; cin >> a >> b; add_edge(a, b); } // 遍歷每個男生嘗試匹配 int res = 0; for (int i = 1; i <= n1; i ++ ) { memset(st, false, sizeof st); // 每次嘗試都會嘗試讓i匹配進來 // 結果res是隻增不減的 if (find(i)) res ++ ; } cout << res << endl; return 0; }

特別要注意每輪要清空st陣列,在這一輪中,嘗試讓女生j匹配給男生x時,就要設定st[j] = true以將j鎖定了,被鎖定的女生j永遠不會解鎖。

匈牙利演算法基於貪心的思想,理論時間複雜度是多項式級別的,但是實際執行速度還是比較快的。