二分图的最大匹配(匈牙利算法)
题目
给定一个二分图,其中左半部包含 \(n_1\) 个点(编号 \(1 \sim n_1\)),右半部包含 \(n_2\) 个点(编号 \(1 \sim n_2\)),二分图共包含 \(m\) 条边。
数据保证任意一条边的两个端点都不可能在同一部分中。
请你求出二分图的最大匹配数。
二分图的匹配:给定一个二分图 \(G\),在 \(G\) 的一个子图 \(M\) 中,\(M\) 的边集 \(\{E\}\) 中的任意两条边都不依附于同一个顶点,则称 \(M\) 是一个匹配。
二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。
输入格式
第一行包含三个整数 \(n_1\)、 \(n_2\) 和 \(m\)。
接下来 \(m\) 行,每行包含两个整数 \(u\) 和 \(v\),表示左半部点集中的点 \(u\) 和右半部点集中的点 \(v\) 之间存在一条边。
输出格式
输出一个整数,表示二分图的最大匹配数。
数据范围
\(1 \le n_1,n_2 \le 500\),
\(1 \le u \le n_1\),
\(1 \le v \le n_2\),
\(1 \le m \le 10^5\)
输入样例:
2 2 4
1 1
1 2
2 1
2 2
输出样例:
2
题解
思路
找寻二分图内左半部分点集与右半部分点集之间的最大匹配数
-
将左半部分\(n_1\)个点看成男生 右半部分\(n_2\)个点看成女生
-
他们其中连的边看成当前男生与女生之间存在好感度可以结为情侣
-
我们要找的就是最大情侣数
怎么满足情侣数最大呢
-
遍历所有男生去搜他们有好感的女生
-
如果当前女生没有匹配其他男生,结为情侣
-
如果匹配了另一个男生但发现他可以去匹配其他女生,那就让他去匹配其他女生,我们匹配当前女生
(ntr)
代码实现
关键点:为什么每次循环内都要清空st[]数组?
for (int i = h[x]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j])
{
match[j] = x;
st[j] = true;
return true;
}
else
{
if (find(match[j]))
{
match[j] = x;
return true;
}
}
}
-
刚开始我是这么写的,这个写法的错误是如果当前女孩已经配对过了,会导致
find(match[j])
无限递归,因为每一次进入find(match[j])
,st[j]
都为true
,每次都会进入else
判断,进入find(match[j])
,从而无限递归 -
如果每次不更新
st[]
为false
,发现match[j]
被赋值过了,然后再去find(match[j])
,进入find(match[j])
内发现st[j] = true
且match[j]
被赋值,虽然match[j]
就是他自己,但是他还是又去find(match[j])
,所以就会一直递归下去。所以每一次搜索男生之前都要清空st[]
发现没有 st数组才是匈牙利算法的精髓
正确算法就可以规避find(match[j])
无限递归的问题,当st[j] = true
的时候就去搜下一个点了,而不是进入错误代码里的else
继续去递归find(match[j])
Tips:如果数组越界了,什么错误都可能发生(TLE RE Segmatation False 等等)
#include <bits/stdc++.h>
using namespace std;
const int N = 510, M = 1e5 + 10; //点的上限与边的上限
int h[N], e[M], ne[M], idx;
int n1, n2, m;
int res;
int st[N]; //用于判断当前右半部分点集是否搜索过(当前女生是否有了男朋友)
int match[N]; //存当前女生匹配的是哪个男生
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++ ;
}
bool find(int x)
{
for (int i = h[x]; i != -1; i = ne[i])
{
int j = e[i]; //这步经常容易忘写 注意!i实际上是指针
if (!st[j]) //如果当前女生没有被搜过
{
st[j] = true;
if (match[j] == 0 || find(match[j])) //如果当前女生没有被匹配 或者 当前女生之前匹配的对象能匹配其他女生 当前女生就能匹配当前男生
{
match[j] = x;
return true;
}
}
}
return false;
}
void solve()
{
scanf("%d%d%d", &n1, &n2, &m);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b); //这里只用构造从男生指向女生的边 因为我们只用枚举男生搜索他有好感的女生就能找出最大匹配数
}
for (int i = 1; i <= n1; i ++ )
{
memset(st, 0, sizeof st); //每次搜下一个男生前清空st数组
if (find(i))
res ++ ;
}
printf("%d\n", res);
return;
}
int main()
{
solve();
return 0;
}