Hopcroft-Karp算法
该算法由John.E.Hopcroft和Richard M.Karp于1973提出,故称Hopcroft-Karp算法。
原理
为了降低时间复杂度,可以在增广匹配集合M时,每次寻找多条增广路径。这样就可以进一步降低时间复杂度,可以证明,算法的时间复杂度可以到达O(n^0.5*m),虽然优化不了多少,但在实际应用时,效果还是很明显的。
基本算法
该算法主要是对匈牙利算法的优化,在寻找增广路径的时候同时寻找多条不相交的增广路径,形成极大增广路径集,然后对极大增广路径集进行增广。在寻找增广路径集的每个阶段,找到的增广路径集都具有相同的长度,且随着算法的进行,增广路径的长度不断的扩大。可以证明,最多增广n^0.5次就可以得到最大匹配。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
#define Inf 0x3f3f3f3f //初始距离
using namespace std;
const int N =3005;//最大点数
vector<int>G[N]; //边关系
int cx[N];//cx[i]表示左集合i顶点所匹配的右集合的顶点序号
int cy[N];//cy[i]表示右集合i顶点所匹配的左集合的顶点序号
int dx[N],dy[N];
int nx,ny;//nx左集合顶点数,ny右集合顶点数
int dis,vis[N];
//寻找增广路径集
int Searchpath(){
queue<int>p;
dis=Inf;
memset(dx,-1,sizeof(dx));
memset(dy,-1,sizeof(dy));
for(int i=1;i<=nx;i++){
//cx[i]表示左集合i顶点所匹配的右集合的顶点序号
if(cx[i]==-1){
//将未匹配的节点 入队 并初始化次节点距离为0
p.push(i);
dx[i]=0;
}
}
//广度搜索增广路径
while(!p.empty()){
int u=p.front();
p.pop();
if(dx[u]>dis) break;
//取右集合顶点
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
//右侧节点的增广路径的距离
if(dy[v]==-1){
dy[v]=dx[u]+1;//v对应的距离 为u对应距离加1
if(cy[v]==-1) dis=dy[v];
else{
dx[cy[v]]=dy[v]+1;
p.push(cy[v]);
}
}
}
}
return dis!=Inf;
}
//寻找路径 深度搜索
int Findpath(int u){
for(int i=0;i<G[u].size();i++){
//如果该点没有被遍历过 并且距离为上一节点+1
int v=G[u][i];
if(!vis[v]&&dy[v]==dx[u]+1){
vis[v]=1;
if(cy[v]!=-1&&dy[v]==dis) continue;
if(cy[v]==-1||Findpath(cy[v])){
cy[v]=u;
cx[u]=v;
return 1;
}
}
}
return 0;
}
//得到最大匹配的数目
int MaxMatch(){
memset(cx,-1,sizeof(cx));
memset(cy,-1,sizeof(cy));
int ans=0;
while(Searchpath()){
memset(vis,0,sizeof(vis));
for(int i=1;i<=nx;i++){
if(cx[i]==-1){
ans+=Findpath(i);
}
}
}
return ans;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
nx=ny=n;
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
G[a].push_back(b);
G[b].push_back(a);
}
printf("%d\n",MaxMatch()/2);
return 0;
}