[模板] 二分图匹配问题——匈牙利算法
Problem
Solution
二分图不一定为无向图,只需边的两端的点可分即是二分图。这是最小边覆盖的前提条件。
匈牙利算法匹配流程:
-
确定二分图的两个点集,选择一个点集作为搜索起点。设为左边点。
-
两个数组:match, check。match代表匹配点,check代表一次深搜的vis(是否被遍历)。match建议初始化为-1。
-
枚举每个左边点。如果左边点没有被匹配过(match为-1),进行深搜匹配。最大匹配不会超过左边点的个数,因此每次深搜最多只增加一对匹配。
I. 枚举相邻的右点。如果没有被匹配过则直接返回,因为已经增加一对匹配。如果被匹配过了则深搜到右点的匹配点,看看是否有点没被匹配,持续下去。必须找到一个没被匹配的右点,才能形成增广路。II. 若找到没被匹配过的点或深搜的点返回了true,更新匹配,返回true。代表这是一个增广路。
III. 若啥也没找到返回false。代表找不到增广路。
-
若深搜左边点返回ture,答案计数+1.代表找到了增广路。
Codes
#include<bits/stdc++.h>
using namespace std;
const int N=2010,M=8e6+10;
int h[N],to[M],nexp[M],p=1;
inline void ins(int a,int b){
nexp[p]=h[a],h[a]=p,to[p]=b,p++;
}
bool check[N];
int match[N];
bool dfs(int x){
for(int u=h[x];u;u=nexp[u]){
if( !check[to[u]] ){
check[to[u]]=1;
if( match[to[u]]==-1 || dfs(match[to[u]]) ){
match[to[u]]=x;
match[x]=to[u];
return true;
}
}
}
return false;
}
int main(){
ios::sync_with_stdio(false);
int n,m,e;
cin>>n>>m>>e;
int u,v;
for(int i=0;i<e;i++){
cin>>u>>v;
if(v>m) continue;
v+=n;
ins(u,v),ins(v,u);
}
memset(match,-1,sizeof(match));
int ans=0;
for(int i=1;i<=n;i++){
if(match[i]==-1){
memset(check,0,sizeof(check));
if(dfs(i)) ans++;
}
}
cout<<ans<<endl;
return 0;
}