二分图最大匹配-匈牙利算法
匈牙利算法是通过找增广路来实现匹配数的增加。当前匹配方案如果再也找不到增广路,则匹配数达到最大。
核心流程:
-
从任意一个未被匹配的点开始。任选一条边(u-v)开始配对。如果v此时没有被配对,则配对成功。
如果v已经被配对了(v-w)。那么先尝试对w进行另一个配对,如果w成功匹配另一个点(w-x),那么
u-v配对成功,我们找到了一条增广路。实际中要用标记数组防止 w-u的情况 :w和u必然在同一边
- 实际上。我们只要对二分图的一边进行匹配,一边匹配完了另一边自然也匹配好了。该算法实际上是对一边(例如左边)进行匹配,详情看洛谷例题
-
如果配对失败那么尝试u的其他配对方案,直到u配对成功或所有方案均尝试为止
-
继续对剩下的顶点进行配对
-
输出配对数
示例问题:CP配对
有2n个人物,m组关系,两个人物之间有关系则可以组成cp,求最大匹配数
数据格式:
- 第一行n,m:表示左右两边各n个人,有m组关系
- 接下来m行,每行两个数字u,v:表示左边的u和右边的v可以组成cp
整体代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| #include<iostream> using namespace std;
int peo, rel;
int num = 0; int counter = 0;
bool dfs(int*match,bool**e,bool*book,int u) { for (int i = 1; i <= peo; i++) { if (book[i] == false && e[u][i] == true) { book[i] = true; if (match[i] == 0 || dfs(match, e, book, match[i]) == true) {
match[i] = u; return true; } } }
return false; }
int main() { cin >> peo >> rel; int* match = new int[peo + 1]; bool** e = new bool* [peo + 1]; bool* book = new bool[peo + 1]; for (int i = 0; i <= peo; i++) { e[i] = new bool[peo + 1]; for (int j = 0; j <= peo; j++) { e[i][j] = false; } match[i] = 0; } int a, b; for (int i = 0; i < rel; i++) { cin >> a >> b; e[a][b] = true; e[b][a] = true; } for (int i = 1; i <= peo; i++) { for (int j = 1; j <= peo; j++) { book[j] = false; } if (dfs(match,e,book,i)) { num++; } }
cout << num;
delete[] match; delete[] book; delete[] e; return 0; }
|
核心代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| bool dfs(int*match,bool**e,bool*book,int u) { for (int i = 1; i <= peo; i++) { if (book[i] == false && e[u][i] == true) { book[i] = true; if (match[i] == 0 || dfs(match, e, book, match[i]) == true) {
match[i] = u; return true; } } }
return false; }
for (int i = 1; i <= peo; i++) { for (int j = 1; j <= peo; j++) { book[j] = false; } if (dfs(match,e,book,i)) { num++; } }
|
洛谷例题:
【模板】二分图最大匹配
题目描述
给定一个二分图,其左部点的个数为 n,右部点的个数为 m,边数为 e,求其最大匹配的边数。
左部点从 1 至 n 编号,右部点从 1 至 m 编号。
输入格式
输入的第一行是三个整数,分别代表 n,m 和 e。
接下来 e 行,每行两个整数 u,v,表示存在一条连接左部点 u 和右部点 v 的边。
输出格式
输出一行一个整数,代表二分图最大匹配的边数。
样例 #1
样例输入 #1
样例输出 #1
样例 #2
样例输入 #2
1 2 3 4 5 6 7 8
| 4 2 7 3 1 1 2 3 2 1 1 4 2 4 1 1 1
|
样例输出 #2
提示
数据规模与约定
对于全部的测试点,保证:
- 1≤n,m≤500。
- 1≤e≤5×104。
- 1≤u≤n,1≤v≤m。
不保证给出的图没有重边。
AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include<iostream> using namespace std;
bool arr[510][510]; bool book[510]; int match[510]; int lefter, righter, edge; int num = 0;
bool dfs(int u) { for (int i = 1; i <= righter; i++) { if (book[i] == false && arr[u][i] == true) { book[i] = true; if (match[i] == 0 || dfs(match[i]) == true) { match[i] = u; return true; } } } return false; }
int main() { cin >> lefter >> righter >> edge; int a, b; for (int i = 0; i < edge; i++) { cin >> a >> b; arr[a][b] = true; }
for (int i = 1; i <= lefter; i++) { for (int j = 1; j <= lefter; j++) { book[j] = false; } if (dfs(i) == true) { num++; } } cout << num; return 0; }
|