二分图的最大匹配
C++
二分图的最大匹配
/*
* 二分图的最大匹配
*
* 先前在介绍二分图时候,我们简述了什么是二分图,以及二分图的充要条件,并学会使用了染色法来判断二分图。
* 此次来介绍二分图的最大匹配。
*
* 定义:
* 二分图的匹配:
* 给定一个二分图 G,在 G 的一个子图 M 中,M 的边集 {E} 中的任意两条边都不依附于同一个顶点,则称 M 是一个匹配。
* 二分图的最大匹配:
* 所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。
* 二分图最大匹配算法:
* 二分图最大匹配算法是基于暴力搜索的算法,同时又使用了 st 数组进行记忆化搜索,因而复杂度控制到了 O(NM)。
*
* res = 0
* 对集合 U 中的每一个点进行遍历
* for u in U:
* 记忆化清空,集合 V
* memset(st, false, sizeof st)
* 如果点 u 可以找到集合 V 中的点 v 相匹配 (这里匹配指的是 v 之前并未和其他点匹配过,
* 或者之前和点 v 匹配的点 u' 可以找到集合中的 v' 配对 -> 这是一个递归的过程
* st 数组,优先剪枝,避免了环形循环)
* res += 1
* 当然,这个 memset 是可以条件性优化的,因为当 u find 为 false 的时候,那么 之前 st 标记为 false 的点,仍旧是没有办法给他腾出一条边。
* u find = true, 才需要 memset(st, false, sizeof st)。
*
*/
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
const int N = 510, M = 100010;
int n1, n2, m;
int h[N], e[M], ne[M], idx;
int match[N];
bool st[N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
// 查看 u 能否在不减少 res 的条件下,多加一条边
bool Find(int u) {
int v;
for (int i = h[u]; ~i; i = ne[i]) {
v = e[i];
if (st[v] == false) {
st[v] = true;
if (match[v] == -1 || Find(match[v])) {
match[v] = u;
return true;
}
}
}
return false;
}
int main()
{
// initialize
memset(h, -1, sizeof h);
memset(ne, -1, sizeof ne);
memset(match, -1, sizeof match);
idx = 0;
// input
scanf("%d%d%d", &n1, &n2, &m);
while (m -- ) {
static int u, v;
scanf("%d%d", &u, &v);
add(u, v);
}
// get the max match
int res = 0;
bool pre = true;
for (int i = 1; i <= n1; i ++ ) {
if (pre == true) {
memset(st, false, sizeof st);
}
pre = Find(i);
if (pre) {
res += 1;
}
}
printf("%d\n", res);
return 0;
}