---
# System prepended metadata

title: 二分圖最大匹配

---

# 二分圖最大匹配

* 一個二分圖中，可以用邊兩兩配對的最大匹配數
* 最小點覆蓋=最大匹配
* 最大獨立集=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)$