學習筆記 匈牙利演算法
阿新 • • 發佈:2020-08-12
眾所周知,二分圖匹配是二分圖理論中的基礎,而匈牙利演算法是一種求解它的基本演算法。事實上,匈牙利演算法是一個十分簡潔的演算法,簡介到讓人感到驚詫。下面就讓我們瞭解一下這個神奇的演算法。
先看幾個定義:
- 匹配 匹配是一個邊的集合,其中任意兩條邊都沒有公共端點。
- 最大匹配 顧名思義,包含邊數最多的匹配
- 交錯路 匹配邊和非匹配邊依次出現的一條路
- 增廣路 從非匹配邊出發,以非匹配邊結束的交錯路(增廣路長度一定是奇數)
你可能已經被這幾個定義搞懵了不過沒關係,慢慢看下去你就懂了。
匈牙利演算法的基本思想,就是不斷地擴大匹配的集合。事實上,他就是一個不斷嘗試讓匹配邊數加一的過程。初始狀態下,所有的邊都是非匹配邊,而當演算法結束時,我們再也不能讓匹配邊數變多了,最大匹配數也就求出來了,還能附帶求出一種方案。
匈牙利演算法基於兩個性質:
- 一個匹配\(M\)是最大匹配,當且僅當圖中不存在增廣路。
- 從某一點出發,如果某個時刻找不到增廣路,那麼你將永遠找不到從該點出發的最短路。
性質1的必要性是顯然的(因為增廣路長度是奇數),我們先把這兩條性質拋開,先來看看這個演算法的過程。
我們首先給二分圖定向(名字挺高大上,其實就是劃分出左部和右部,規定增廣路從左部出發)。列舉每一個左部點(想想這個點會不會已經被匹配),如果找不到增廣路就算了,如果找到了增廣路,我們就把這條路徑上所有邊的狀態取反,於是我們就得到了一種大小+1的匹配。結合程式碼理解一下 複雜度\(O(nm)\)
int vis[mxn],mch[mxn];//match inline bool hungary(int x){ if(vis[x])return 0;vis[x]=1; //vis記錄左部點右部點皆可 for(int i=first[x];i;i=nxt[i]) //邊是單向邊 if(!mch[to[i]] || hungary(mch[to[i]]))return mch[to[i]]=x,1; return 0; } //main函式 int ans=0; for(int i=1;i<=n;++i)memset(vis,0,sizeof(vis)),ans+=hungary(i);
如果你沒看懂以上程式碼,那隻能是心理原因 我們回過頭看這兩個性質,性質1顯然是演算法的基礎,而性質2確保了每個點只搜一次。至此我們已經完全理解了匈牙利演算法的內在邏輯。
有沒有覺得這兩個性質很神奇?如果感興趣可以看看這篇論文,挺好理解的,性質2尤其好理解,有不懂的名詞可以百度一下。再次強調,匹配是邊集。
好像還有bfs實現的版本,據說常數更小。
以上。