[筆記]二分圖最大匹配--匈牙利演算法
阿新 • • 發佈:2020-08-22
[筆記]二分圖最大匹配--匈牙利演算法
原題鏈
演算法:
0.首先有一些概念:
①.二分圖:設G=(V,E)是一個無向圖,如果頂點V可分割為兩個互不相交的子集(A,B),並且圖中的每條邊(i,j)所關聯的兩個頂點i和j分別屬於這兩個不同的頂點集(i in A,j in B),則稱圖G為一個二分圖。(我並沒有看懂上面這一坨,我的理解是可以分成兩個相互獨立部分的圖就是二分圖)
②匹配:給定一個二分圖G,在G的一個子圖M中,M的邊集{E}中的任意兩條邊都不依附於同一個頂點,則稱M是一個匹配。
③:最大匹配:最大匹配即是選擇其中邊數最大的子集的圖。
④:增廣路:若P是圖G中一條連通兩個未匹配頂點的路徑,並且屬於M的邊和不屬於M的邊(即已匹配和待匹配的邊)在P上交替出現,則稱P為相對於M的一條增廣路徑(舉例來說,有A、B集合,增廣路由A中一個點通向B中一個點,再由B中這個點通向A中一個點……交替進行)
我們通過以上概念可以知道,二分圖的一組匹配是最大匹配當且僅當圖中不存在增廣路.
1.演算法步驟
①首先所有邊都是非匹配邊
②尋找增廣路,並將增廣路兩端的點設為已匹配的點
③重複步驟②,知道圖中不再有增廣路
2.詳細過程
具體說一說第②步:
要想新建一條增廣路需要滿足一下兩個條件中的任意一個:
1.右半部分的點y自身為非匹配點
2.右部分的點y為匹配點,但是與它相連的左部分的點x可以在右部分找到另一個點進行匹配
AC程式碼
#include <bits/stdc++.h> using namespace std; bool mapp[5200][5200],vis[5200]; int match[5200];//記錄左部分匹配的物件 int n,m,s; bool dfs(int k){ for(int i = n + 1;i <= n + m;i++){ if(!vis[i] && mapp[k][i]){//兩點之間有連邊,同時沒有被訪問過 vis[i] = true; if(!match[i] || dfs(match[i])){//滿足上面說的兩個條件 match[i] = k; match[k] = i; return true; } } } return false; } int main(){ cin>>n>>m>>s; for(int i = 1;i <= s;i++){ int x,y; cin>>x>>y; if(x > n || y > m)continue; y += n;//處理一下,因為題中給的左右部分的編號相同,不方便標記是否訪問過 mapp[x][y] = mapp[y][x] = true; } int ans = 0; for(int i = 1;i <= n;i++){ memset(vis,false,sizeof(vis)); for(int j = 1;j <= n;j++)//預設從左部分向右部分匹配,因此將左部分預設成已訪問過 vis[j] = true; if(dfs(i))ans++; } cout<<ans<<endl; return 0; }