任务:给定一个二分图,用Hopcroft-Karp算法求这个二分图的最大匹配数。
说明:
dx[/],dy[/]
d
x
[
/
]
,
d
y
[
/
]
分别表示二分图左右部顶点的距离标号;
mx[/],my[/]
m
x
[
/
]
,
m
y
[
/
]
分别表示二分图左右部顶点的匹配节点;
Hopcroft相比普通的匈牙利算法来说,由于每次是增广一系列路径,所以更快。我们每次从所有未匹配的左部节点开始
BFS
B
F
S
,进行距离标号。对于每一个队列中的左部节点
X
X
,考虑与他相邻的所有右部节点:如果
Y
Y
是一个未匹配的右部节点,则说明至少还存在一条增广路,用一个变量
flag
f
l
a
g
记录,以便之后增广;否则,将
Y
Y
的匹配节点加入到队列中,顺便求出距离标号。当结束时,若不存在增广路(即
flag
f
l
a
g
为
false
f
a
l
s
e
),那么算法结束;否则对于每一个没有匹配的左部节点
X
X
执行匈牙利算法的操作,在这里,
find(X)
f
i
n
d
(
X
)
过程中,只考虑这样的边
(u,v)
(
u
,
v
)
:满足
dx[u]+1=dy[v]
d
x
[
u
]
+
1
=
d
y
[
v
]
。
模板:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
const int maxn = 50000;
int n1, n2;
vector<int>g[maxn + 10]; //实现邻接表;g[i]表示与左边点i相连的右边的点;
int mx[maxn + 10], my[maxn + 10];
queue<int> que;
int dx[maxn + 10], dy[maxn + 10];
bool vis[maxn + 10];
bool find(int u)
{
for(int i = 0; i < g[u].size(); i++)
{
if(!vis[g[u][i]] && dy[g[u][i]] == dx[u] + 1)
{
vis[g[u][i]] = true;
if(!my[g[u][i]] || find(my[g[u][i]])) {
mx[u] = g[u][i];
my[g[u][i]] = u;
return true;
}
}
}
return false;
}
int matching(){
memset(mx, 0, sizeof(mx));
memset(my, 0, sizeof(my));
int ans = 0;
while(true)
{
bool falg = false;
while(!que.empty()) que.pop();
memset(dx, 0, sizeof(dx));
memset(dy, 0, sizeof(dy));
for(int i = 1; i <= n1; i++)
if(!mx[i]) que.push(i);
while(!que.empty())
{
int u = que.front();
que.pop();
for(int i = 0; i < g[u].size(); i++)
{
if(!dy[g[u][i]]){
dy[g[u][i]] = dx[u] + 1;
if(my[g[u][i]]) {
dx[my[g[u][i]]] = dy[g[u][i]] + 1;
que.push(my[g[u][i]]);
}
else {
flag = true;
}
}
}
}
if(!flag) break;
memset(vis, 0,sizeof(vis));
for(int i = 1; i <= n1; i++)
if(!mx[i] && find(i)) ans++;
}
return ans;
}