# 问题描述

给定一个二分图,其中左半部包含 n1 个点(编号 1∼n1),右半部包含 n2 个点(编号 1∼n2),二分图共包含 m 条边。

数据保证任意一条边的两个端点都不可能在同一部分中。

请你求出二分图的最大匹配数

1、二分图:在一个图中,可以把所有的点划分到两个集合,满足所有的边都在两个集合之间连通,我们认为这个图是二分图。

2、二分图的匹配:给定一个二分图 G,在 G 的一个子图 M 中,M 的边集 {E} 中的任意两条边都不依附于同一个顶点,则称 M 是一个匹配。

3、二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。

输入格式

第一行包含三个整数 n1、 n2 和 m。

接下来 m 行,每行包含两个整数 u 和 v,表示左半部点集中的点 u 和右半部点集中的点 v 之间存在一条边。

输出格式

输出一个整数,表示二分图的最大匹配数。

# 算法描述

为什么要管这个算法叫 “恋爱循环” 呢?因为它的算法流程和这个名字实在是太像了。这个算法真正的名字叫 “匈牙利算法”,也叫 “增广路算法”。

在这里,我们把一个集合当作男同学集合,一个当作女同学集合。可以连成边的男女同学,就认为是具有好感的。

我们作为 “月老”,任务就是把两个集合中的所有同学两两配对,组合出尽可能多的情侣。

# 算法流程

遍历整个男同学集合,从第一个男同学开始,寻找和他具有好感的女同学,只要找到了就暂时把他俩配成一对。

如果和当前男同学 X 具有好感的女同学 A 已经被配对给了别的同学怎么办呢?此时比较常规的做法是继续去和下一个具有好感的女同学配对。

但是,该算法的做法是找到和这位女同学 A 匹配的男同学 B 是哪位,看下这位男同学 B 是否有其他的女同学 C 可以配对,如果有就让这位男同学 B 和其他人 C 配对。这样当前男同学 X 就可以和女同学 A 进行配对啦。

# 示例

1、遍历到 1 号男同学,发现他和 2 号女同学具有好感,因此,我们先将他俩连一条红绳。

2、遍历到 2 号男同学,发现他和 1 号女同学具有好感,因此,他俩也可以连一条红绳。

3、遍历到 3 号男同学,发现他和 2 号女同学具有好感,2 号女同学已经和 1 号男同学配对了。

此时关键来了,再看 1 号男同学发现他还和 4 号女同学具有好感,那么问题就简单了。我们给 1 号男和 2 号女一条绿绳子,让他们分开。

4、然后再把 1 号男和 4 号女配对,3 号男和 2 号女配对,皆大欢喜。

5、最后把 4 号男和 3 号女配对。获得最大匹配数,4 对。

# Code

from collections import defaultdict
# 读入数据 n1 是男同学数量,n2 是女同学数量
n1, n2, m = map(int, input().split())
# 保存具有好感度的同学
edge = defaultdict(list)
for _ in range(m):
    a, b = map(int, input().split())
    # 注意:此处因为我们后面只需要通过男同学寻找女同学
    # 所以不需要存女同学对应的男同学。
    edge[a].append(b)
    
match = [0] * (n2 + 1)  # 女同学匹配到了那个男同学
# 给当前 boy 匹配个 girl
def dfs(boy):
    # 与当前 boy 互有好感的所有 girl
    girls = edge[boy]
    for girl in girls:
        # 如果当前 gilr 未和当前 boy 匹配过
        if not status[girl]:
            # 记录这个 girl 已经被当前 boy 匹配了
            status[girl] = True
            # 核心代码 如果当前 girl 没有对象直接把她给当前 boy
            # 如果有对象 就 dfs 给他对象找新的 girl
            if not match[girl] or dfs(match[girl]):
                match[girl] = boy
                return True
    # 如果当前 boy 未能匹配任何妹子 循环结束
    return False
res = 0
for i in range(1, n1 + 1):
    # 记录当前 boy 已经匹配过的 girl,避免 dfs 重复匹配相同 girl
    status = [0] * (n2 + 1)
    if dfs(i):
        res += 1
print(res)

# 时间复杂度

从思路上分析,最外层循环我们遍历了每一个男同学,而匹配女同学的时候,最坏的情况就是被迫把所有的女同学都扫描一遍,所以总的时间复杂度为 O (NM)。