### 匈牙利算法及其应用
#### 一、概述
匈牙利算法是一种解决二分图最大匹配问题的有效算法。二分图(Bipartite Graph)是指一个图中的节点可以分为两个互不相交的集合,使得每条边都连接这两个集合中的一个节点与另一个节点。匈牙利算法主要应用于寻找二分图中的最大匹配,即在这个图中找到最大的两两不相交的边的集合。
#### 二、关键概念
1. **二分图**:由两个互不相交的顶点集组成的图,图中的每条边都连接着这两个集合中的节点。
- 判断方法:
- 点集是否可以被划分为两个独立的点集。
- 图中是否存在长度为奇数的环(如果没有,则是二分图)。
2. **匹配**:在一个图中,匹配是指一组没有公共端点的边的集合。特别地,如果一个匹配包含了尽可能多的边,则称其为最大匹配。如果最大匹配的边的数量等于图中顶点数量的一半,则称之为完美匹配。
3. **边覆盖**:在图中,边覆盖是指一组边,它们能够覆盖图中的所有顶点。边覆盖中边的数量越少越好,因此我们关注的是最小边覆盖。
4. **独立集**:在一个图中,独立集是一组顶点,这些顶点之间不存在边相连。最大的独立集是指包含的顶点数量最多的独立集。
5. **顶点覆盖**:顶点覆盖是指一组顶点,这些顶点能够覆盖图中的所有边。顶点覆盖中顶点的数量越少越好,因此我们关注的是最小顶点覆盖。
#### 三、性质
- 对于不存在孤立点的图,有 |最大匹配| + |最小边覆盖| = |V|。
- 对于任意图,有 |最大独立集| + |最小顶点覆盖| = |V|。
- 在二分图中,有 |最大匹配| = |最小顶点覆盖|。
#### 四、算法实现
匈牙利算法通过不断地尝试构建增广路径来寻找最大匹配。具体步骤如下:
1. **初始化**:为每个顶点创建一个初始状态,如匹配情况。
2. **构建增广路径**:从未匹配的顶点出发,沿着可能的边进行深度优先搜索或广度优先搜索,试图找到一个新的匹配。
3. **更新匹配**:一旦找到了增广路径,就更新图中的匹配关系。
4. **重复过程**:重复上述过程,直到无法找到新的增广路径为止。
下面给出一个简单的匈牙利算法模板:
```cpp
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <map>
#include <cmath>
#include <stack>
using namespace std;
const int maxn = 1e4 + 5;
int n, m, ecnt = 0, head[maxn << 1], match[maxn << 1], u, v;
bool f[maxn << 1];
// 初始化
void init() {
for (int i = 1; i <= 2 * n; i++) head[i] = -1, match[i] = -1;
}
// 边的结构体
struct node {
int to, nxt;
} edge[2 * maxn];
// 添加边
void add(int from, int to) {
edge[ecnt].to = to;
edge[ecnt].nxt = head[from];
head[from] = ecnt++;
}
// 深度优先搜索
bool dfs(int u) {
for (int i = head[u]; i != -1; i = edge[i].nxt) {
int v = edge[i].to;
if (f[v] == 1) continue;
f[v] = 1;
if (match[v] == -1 || dfs(match[v])) {
match[v] = u;
return true;
}
}
return false;
}
// 计算最大匹配
bool cac() {
for (int i = 1; i <= n; i++) {
for (int j = n + 1; j <= 2 * n; j++) f[j] = 0;
if (!dfs(i)) {
return false;
}
}
return true;
}
int main() {
scanf("%d%d", &n, &m);
init();
for (int i = 1; i <= m; i++) {
scanf("%d%d", &u, &v);
u++; v++;
add(u, v + n);
}
if (cac()) printf("YES\n");
else printf("NO\n");
return 0;
}
```
### 应用实例
#### 实例一:最小顶点覆盖
假设有一个问题,需要确定最少的顶点数以覆盖所有的问题(这里的“问题”可以理解为图中的边)。可以通过将问题转化为一个最小顶点覆盖问题来解决,即在二分图中找到能够覆盖所有边的最少顶点数。例如,`poj3041` 这个问题就可以通过匈牙利算法求解。
#### 实例二:最大独立集
另一种应用是最大独立集问题,即在一个图中选择尽可能多的顶点,使得这些顶点之间不存在任何边相连。例如,在 `punchpower` 这个问题中,可以通过建立二分图,并使用匈牙利算法来求解最大独立集。
以上就是匈牙利算法的基本介绍及其应用场景,通过理解和掌握匈牙利算法的核心思想,可以有效地解决一系列图论问题。