📅  最后修改于: 2023-12-03 14:55:19.126000             🧑  作者: Mango
Hopcroft–Karp 算法是一种算法,用于在二分图中寻找最大匹配。这是一种增量方法,其时间复杂度为 $O(E \sqrt{V})$。
Hopcroft–Karp 算法本质上是一个 BFS(广度优先搜索)算法,每次 BFS 都会沿着交替路找到一条增广路。Hopcroft–Karp 算法有两个阶段:第一阶段找到初始匹配和交替路径图;第二阶段寻找最大匹配。
Hopcroft–Karp 算法可以分类为增量算法(incremental algorithms)或增加路径算法(augmenting path algorithm)。
下面是一个基于 Hopcroft–Karp 算法的实现。这个实现使用了 C++ 语言。此处的代码只包含了第二阶段的实现,因为第一阶段的实现比较简单。
const int MAXN = 100005;
const int MAXM = 500005;
const int INF = 0x3f3f3f3f;
struct edge {
int v, nxt; // v 是边指向的点的编号,nxt 则是下一条与 u 相连的边的编号
} e[MAXM << 1];
int tot = 1, head[MAXN], dis[MAXN], match[MAXN], n, m, s, t; // head 存储链式前向星连接表的表头,dis 数组记录到源点的距离,match 记录每个点已匹配的对象的编号
void add(int u, int v) { // 向链式前向星的连接表中添加一条从 u 到 v 的边
e[++tot] = edge{v, head[u]};
head[u] = tot;
}
bool bfs() { // 重构交替路
queue<int> q;
memset(dis, INF, sizeof(dis)); // 初始化距离数组
for (int i = 1; i <= n; ++i)
if (match[i] == 0) {
dis[i] = 0; // 将源点添加到队列中,并把距离设为 0
q.push(i);
}
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (dis[match[v]] == INF) { // 如果该点所匹配的点未被访问过,则加入队列
dis[match[v]] = dis[u] + 1;
q.push(match[v]);
}
}
}
return dis[t] != INF; // 如果汇点的距离被更新了,则找到到达汇点的一条增广路
}
bool dfs(int u) { // 查找交替路
if (u == t)
return true;
for (int i = head[u]; i; i = e[i].nxt) { // 枚举与 u 相连的点
int v = e[i].v;
if (dis[match[v]] == dis[u] + 1 && dfs(match[v])) { // 如果它所匹配的点在增广路上没出现过,则加入
match[u] = v;
match[v] = u;
return true;
}
}
dis[u] = INF; // 如果该点无法到达汇点,则把它到源点的距离设为 INF(表示已遍历)
return false;
}
int hopcroft_karp() {
int res = 0;
while (bfs()) // 只要存在增广路,就重构交替路
for (int i = 1; i <= n; ++i)
if (match[i] == 0 && dfs(i)) // 只要已经匹配的边没有与之关联的交替路,就把这条边加入到交替路里
++res;
return res;
}
Hopcroft–Karp 算法是求一个二分图最大匹配的经典算法,虽然其时间复杂度只为 $O(E\sqrt{V})$,但由于其难以实现以及空间复杂度等问题,实际应用不太广泛。