匈牙利演算法總結
二分圖:
定義:如果一個圖的所有頂點可以被分為X和Y兩個集合,並且所有邊的兩個頂點恰好一個屬於集合X,另一個屬於集合Y,即每個集合內的頂點沒有邊相連,那麼此圖就是二分圖。
很多問題都可以轉化為二分圖匹配模型來計算。二分圖有如下幾種常見變形:
(1)最小頂點覆蓋
選取最少的點(X或Y中都行),讓每條邊都至少和其中一個點關聯。
Knoig定理:二分圖的最小頂點覆蓋數等於二分圖的最大匹配數。
(2)最小路徑覆蓋
對於一個DAG(有向無環圖),選取最少條路徑,使得每個頂點屬於且僅屬於一條路徑。
結論:DAG圖的最小路徑覆蓋數 = 節點數(n) - 最大匹配數(m).
(3)最大獨立集
選取最多的點,使任意所選兩點均不相連。
結論:二分圖的最大獨立集數 = 節點數(n) - 最大匹配數(m).
匈牙利演算法:
匈牙利演算法就是一種用增廣路徑求二分圖最大匹配的演算法,核心是尋找增廣路徑。
演算法的時間複雜度為O(NM),其中N為二分圖左邊的頂點數,M為二分圖中邊的數目。
基本步驟:
1.首先從任意一個未被配對的點 u開始,從點u的邊中任意選一條邊(假設這條邊是u->v)開始配對。如果此時點V還沒有被配對,則配對成功,此時便找到了一條增廣路(只不過這條增廣路比較簡單)。如果此時點v已經被配對了,那就要嘗試進行“連鎖反應”。如果嘗試成功了,則找到一條增廣路,此時需要更新原來的配對關係。這裡要用一個數組match來記錄配對關係,比如點v與點u配對了,就記作match[v]=u。配對成功後,記得要將配對數加1。配對的過程我們可以通過深度優先搜尋來實現,當然廣度優先搜尋也可以。
2.如果剛才所選的邊配對失敗,要從點u的邊中再重新選一條邊, 進行嘗試。直到點u配對成功,或者嘗試過點u所有的邊為止。
3.接下來繼續對剩下沒有被配對的點一一進行配對, 直到所有的點都嘗試完畢,找不到新的增廣路為止。
核心程式碼:
for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) vis[j] = 0;//清空上次搜尋時的標記 if (dfs(i)) sum++;//尋找增廣路,如果找到,配對數加1 } int dfs(int u) { for (int i = 1; i <= n; i++) { if (!vis[i] && map[u][i]) { vis[i] = 1;//標記點i已經訪問過 //如果點i未被配對或者找到了新的配對 if (!match[i] || dfs(match[i])) { //更新配對關係 match[i] = u; return 1; } } } return 0; }