匈牙利算法:是一种在多项式时间内求解任务分配问题的组合优化算法,并推动了后来的原始对偶方法
时间复杂度:O(nm)
适用场景:二分图的最大匹配
核心思想:增广路径,即当左边集合的点1匹配右边的点2为已匹配时,会找到上一个左边指向该右边已匹配点2的节点3,并尝试将该节点3转向另一个节点4,若果节点4也匹配则重复该操作,如果未匹配则称这条新路径为增广路径
二分图:又称作二部图,是图论中的一种特殊模型。图中的所有顶点可以分为两个集合,集合内部没有边存在,边只存在于集合之间,即所有边的顶点分别属于不同集合
二分图特性:二分图一定不含有奇数环
二分图只有偶数环,因为只有偶数环才可以保证成环的条件下还能保证在同一个集合内的环节点没有边存在,而奇数环的出现说明在这个环中至少有两个节点是在同一集合内,并且它们之间还存在边
最大匹配:每条边的两端都是不同集合的节点,在节点不重复的情况下,边数最多
参考代码:
例题:二分图的最大匹配
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
//题目给定的点数、边数限制
const int N = 510, M = 100010;
//输入的左边集合点数n1,右边集合点数n2以及边数m
int n1,n2,m;
//存储边数的邻接表
int h[N],e[M],ne[M],idx;
//下标表示右边集合节点x,其值表示左边集合节点y
int match[N];
//存储右边集合是否已匹配
bool st[N];
void add(int a,int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx;
idx ++ ;
}
bool find(int x){
//对传入的节点进行遍历所有可匹配的右边点
for(int i = h[x] ; i != -1 ; i = ne[i]){
int j = e[i];
//如果右边节点未匹配
if(!st[j]){
//则将右边节点设为已匹配
st[j] = true;
//如果右边节点为未匹配或者为右边节点的现任节点找到新的匹配点
if(match[j] == 0 || find(match[j])){
//将右边节点的匹配对象换成自己
match[j] = x;
//返回配对成功
return true;
}
}
}
//返回配对失败
return false;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin >> n1 >> n2 >> m;
int a,b;
memset(h,-1,sizeof h);
while (m--){
cin >> a >> b;
//仅需存储从左到右的边即可,只对左边集合节点进行操作
add(a,b);
}
//存储匹配数
int res;
//对左边集合的节点进行遍历
for(int i = 1 ; i <= n1 ; i++){
memset(st,false,sizeof st);
if(find(i)){
res++;
}
}
cout << res << endl;
return 0;
}