Unverified Commit e974a8e1 authored by ir1d's avatar ir1d Committed by GitHub
Browse files

添加了二分图匹配中的匈牙利算法 (#1907)

添加了二分图匹配中的匈牙利算法
parents 17b8f608 ae82e069
Loading
Loading
Loading
Loading
+87 −2
Original line number Diff line number Diff line
@@ -31,12 +31,97 @@

### 二分图匹配

#### 霍尔定理
#### 匹配

设二部图 $G=<V_1, V_2, E>, |V_1| \leq |V_2|$ ,则 $G$ 中存在 $V_1$ 到 $V_2$ 的完备匹配当且仅当对于任意的 $S \subset V_1$ ,均有 $|S|\leq|N(S)|$ ,其中 $N(S)=\Cup_{v_i \in S}{N(V_i)}$ ,是 $S$ 的邻域
给定一个二分图$G$,若在$G$的子图$M$中,任意两条边都没有公共节点,那么称$M$为二分图$G$的一个匹配,且M的边数为匹配数

#### 最大匹配

寻找二分图边数最大的匹配称为最大匹配问题。

对此,有解决此问题的**匈牙利算法**,时间复杂度为$O(NM)$。

算法步骤大致如下:

1.首先从任意一个未配对的点$u$开始,选择他的任意一条边($u$-$v$),如此时$v$还未配对,则配对成功,配对数加一,若$v$已经配对,则尝试寻找$v$的配对的另一个配对(该步骤可能会被递归的被执行多次),若该尝试成功,则配对成功,配对数加一。

2.若果上一步配对不成功,那么选择重新选择一条未被选择过的边,重复上一步。

3.对剩下每一个没有被配对的点执行步骤1,直到所有的点都尝试完毕。

用下面的二分图为例:

![](./images/bi-graph-1.png)

先对节点1和2尝试匹配,假设他们分别找到了4和5。

![](./images/bi-graph-2.png)

接下来对节点3尝试匹配,选择边(3-4),但发现4已经有匹配了,我们尝试寻找4的匹配的其他匹配,即1的其他匹配。
这个匹配显然只能从未被选择的边里找(灰色的),我们可以遍历1的所有边,寻找未被选择的,很容易找到边(1-5)。

我们发现5已经被匹配了,所以我们尝试寻找5的匹配的其他匹配,即2的其他匹配。类似的,可以找到6。

![](./images/bi-graph-3.png)

于是我们得到了新的匹配方案,且该方案比之前的匹配数多一。

![](./images/bi-graph-4.png)

可以发现,当尝试对节点3进行匹配时,走过了一条路径(3-4-1-5-2-6),最后找到了新的匹配方案,我们把这样的道路叫做**增广路**,其本质是一条起点和终点都是未匹配节点的路径。

匈牙利算法执行的过程也可以看作是不断寻找增广路的过程,当在当前匹配方案下再也找不到增广路,那么当前匹配方案便是最大匹配了。

代码如下:

```cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
int n, m, e;
vector<int> G[N]; //使用邻接表来储存边
int match[N], vis[N];
bool dfs(int u){
    int len = G[u].size();
    for(int i = 0; i < len; i++){ //遍历每一条边
        int v = G[u][i];
        if(vis[v]) continue;
        vis[v] = 1;
        if(!match[v] || dfs(match[v])){ //如果v没有匹配,或者v的匹配找到了新的匹配
            match[v] = u;
            match[u] = v; //更新匹配信息
            return 1;
        }
    }
    return 0;
}
int main(){
    scanf("%d %d %d", &n, &m, &e);
    for(int i = 1; i <= e; i++){
        int a, b;
        scanf("%d %d", &a, &b);
        if(a > n || b > m) continue;
        G[a].push_back(n + b);
        G[n + b].push_back(a);
    }
    int ans = 0;
    for(int i = 1; i <= n; i++){ //对每一个点尝试匹配
        for(int j = 1; j <= n + m; j++) vis[j] = 0;
        if(dfs(i)) ans++;
    }
    printf("%d", ans);
    return 0;
}
```

例题参考:

[洛谷 P3386 【模板】二分图匹配](https://www.luogu.com.cn/problem/P3386)

#### 最大权匹配

#### 霍尔定理

设二部图 $G=<V_1, V_2, E>, |V_1| \leq |V_2|$ ,则 $G$ 中存在 $V_1$ 到 $V_2$ 的完备匹配当且仅当对于任意的 $S \subset V_1$ ,均有 $|S|\leq|N(S)|$ ,其中 $N(S)=\Cup_{v_i \in S}{N(V_i)}$ ,是 $S$ 的邻域。

## 一般图匹配
+17.5 KiB
Loading image diff...
+17.4 KiB
Loading image diff...
+16.9 KiB
Loading image diff...
+17.9 KiB
Loading image diff...