# 二分圖最大匹配 * 一個二分圖中,可以用邊兩兩配對的最大匹配數 * 最小點覆蓋=最大匹配 * 最大獨立集=n-最大匹配 > 如果一張圖可以分成兩群,而這兩群的邊都是連到另一群的,也就是不會連到自己 Augmenting Path Algorithm --- :::info 交錯路徑:一條路徑中,已配對的邊和沒配對的邊相互交錯連接 增廣路徑:一個交錯路徑,但是頭尾都是未配對的邊 ::: * 增廣路徑中,把已配對的邊變成未配對,未配對變成已配對,這時配對數量一定會增 加1個(**種樹問題**) * 原本的增廣路徑路徑: ![](https://i.imgur.com/q7H2owv.png) * 互換後的增廣路徑: ![](https://i.imgur.com/ZOR30hW.png) * 所以作法是:枚舉每個左邊的點,dfs找從這一點開始的增廣路徑。如果找到就加一(包只有一個邊但未配對的),然後遞迴回去把配對互換。 * 如果找不當增廣路徑的話就結束 * $O(mn)$ ```cpp= #include <bits/stdc++.h> #define EB emplace_back #define vci vector<int> #define PII pair<int,int> #define F first #define S second #define rep(X, a,b) for(int X=a;X<b;++X) #define ALL(a) (a).begin(), (a).end() #define SZ(a) (int)(a).size() #define NL "\n" #define LL long long using namespace std; //lm: left node pairs, right node pairs, done:visited vector<int> lm, rm, done; vector<vector<int>> lg;// edges from the left bool dfs(int nd){ done[nd]=1; for(auto a:lg[nd]){ if(rm[a]==-1 || (!done[rm[a]] && dfs(rm[a]))){ lm[nd]=a; rm[a]=nd; return 1; } } return 0; } int main(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, m, k; // left nodes:n, right nodes:m, edges:k cin>>n>>m>>k; lm.resize(n, -1); rm.resize(m, -1); done.resize(n); lg.resize(n); int a, b; rep(i,0,k){ cin>>a>>b; a--, b--; lg[a].EB(b); } LL ans=0; rep(i,0,n){ fill(ALL(done), 0); if(dfs(i)) ans++; //有增廣路徑 } cout<<ans<<NL; } ``` 網路流 --- * 問題可以轉為最大網路流 * 設一個起點,連到二分圖左邊所有的點 * 二分圖右邊所有點則連到一個終點 * 所有邊(包括二分圖裡)都設最大流量為1 * 因此每個左邊每個點最多只會從起點連進1個邊,然後右邊所有的點最多只有一個邊能流出去到終點 ![](https://i.stack.imgur.com/MjMn3.jpg) * dinic: $O(\sqrt{n}m)$