也叫”月老”算法。用于计算一个给定的二分图的最大匹配。

基本概念:

二分图的匹配:给定一个二分图 G,在 G 的一个子图 M 中,M 的边集 {E} 中的任意两条边都不依附于同一个顶点,则称 M 是一个匹配。 二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。

匹配:选边的数量且选择的所有边没有重复节点。
最大匹配:边数最多的匹配
匹配点:指选择的边的端点
增广路径:从一个非匹配点出发,先走非匹配边,再走非匹配边,以此类推,最终走到某个非匹配点的路径就是一个增广路径。

最大匹配等价于不存在增广路径

模板

861. 二分图的最大匹配
给定一个二分图,其中左半部包含 n1 个点(编号 1∼n1),右半部包含 n2 个点(编号 1∼n2),二分图共包含 m 条边。
数据保证任意一条边的两个端点都不可能在同一部分中。
请你求出二分图的最大匹配数。

输入格式
第一行包含三个整数 n1、 n2 和 m。
接下来 m 行,每行包含两个整数 u 和 v,表示左半部点集中的点 u 和右半部点集中的点 v 之间存在一条边。
输出格式
输出一个整数,表示二分图的最大匹配数。
数据范围
1≤n1,n2≤500
1≤u≤n1
1≤v≤n2
1≤m≤105
输入样例:

  1. 2 2 4
  2. 1 1
  3. 1 2
  4. 2 1
  5. 2 2

输出样例:

  1. 2

思路:
遍历左半部分每个点i,为其在右半部分寻一个对象(即遍历i的每条边)。
如果i中意的对象没有被选,那就将其匹配给i
如果i中意的对象已经被选,尝试让其原配重新选一个
它的原配能再选一个其他的,它就与i匹配成功
它的原配不能再选其它的了,它与i匹配失败,i继续尝试其它的
如果i选无可选,匹配失败,i打光棍吧

时间复杂度:O(nm)

  1. import java.util.*;
  2. public class Main {
  3. static final int N = 510, M = 100010;
  4. static int[] h = new int[N], e = new int[M], ne = new int[M];
  5. static int idx;
  6. static int[] match = new int[N];
  7. static boolean[] st = new boolean[N];
  8. static int n1, n2, m;
  9. public static void main(String[] args) {
  10. Scanner sc = new Scanner(System.in);
  11. n1 = sc.nextInt();
  12. n2 = sc.nextInt();
  13. m = sc.nextInt();
  14. Arrays.fill(h, -1);
  15. for (int i = 0; i < m; i++) {
  16. int a = sc.nextInt(), b = sc.nextInt();
  17. add(a, b);
  18. }
  19. int cnt = 0;
  20. for (int i = 1; i <= n1; i++) {
  21. Arrays.fill(st, false);
  22. if (find(i))
  23. cnt++;
  24. }
  25. System.out.println(cnt);
  26. }
  27. static void add(int a, int b) {
  28. e[idx] = b;
  29. ne[idx] = h[a];
  30. h[a] = idx++;
  31. }
  32. static boolean find(int u) {
  33. for (int i = h[u]; i != -1; i = ne[i]) {
  34. int j = e[i];
  35. if (!st[j]) {
  36. st[j] = true;
  37. if (match[j] == 0 || find(match[j])) {
  38. match[j] = u;
  39. return true;
  40. }
  41. }
  42. }
  43. return false;
  44. }
  45. }