二分图#
定义#
二分图#
对于一个无向图 ,可以将 分为两个不相交的子集,使得任意一条边的顶点分属两个不同的点集,那么这个无向图是二分图。
匹配#
对于无向图 ,取边集的子集 ,使得 中任意两条边没有公共顶点,那么 是图 的一个匹配,又称边独立集。
最大匹配#
边数最多的匹配。极大匹配的边数唯一,取法可能不唯一。
匹配边 & 非匹配边#
在边独立集中的边是匹配边,反之是非匹配边。
匹配点 & 非匹配点#
能被匹配边连接到的点是匹配点,反之是非匹配点。
完美匹配#
若 是图 的一个匹配,且 中的每个顶点都是匹配点,那么 是 的完美匹配。
交错路径#
图 中,由匹配边和非匹配边交替构成的路径,是交错路径。
增广路径#
起点和终点都是非匹配点的交错路径,是增广路径。
显然, 是 的最大匹配,当且仅当 中不含关于 的增广路径。
因为如果 中含有关于 的增广路径,把增广路径上的匹配边取反,可以使匹配大小增加.
二分图最大匹配#
概述#
在二分图中找到一个最大匹配。
Hall定理#
二分图 ,其中 ,则 中存在 到 的完美匹配,当且仅当 中任意 个顶点,都至少与 中的 个顶点相连。
反证即可。
思想#
对一条增广路进行异或之后,能使匹配中的边数增加 .
异或之后,可以得到:
匈牙利算法#
- 将初始的匹配 置为 .
- 找到一条增广路径,通过异或操作更新最大匹配。
- 重复2.
时间复杂度:
代码(From Jisuanke):
const int MAX_N = 100;
const int MAX_M = 10000;
struct Edge {
int v, next;
} e[MAX_M];
int p[MAX_N], eid;
void init() {
memset(p, -1, sizeof(p));
eid = 0;
}
void insert(int u, int v) {
e[eid].v = v;
e[eid].next = p[u];
p[u] = eid++;
}
bool vst[MAX_N];
int rmatch[MAX_N];
bool dfs(int u) {
for(int i = p[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(!vst[v]) {
vst[v] = true;
if(rmatch[v] == -1 || dfs(rmatch[v])) {
rmatch[v] = u;
return true;
}
}
}
return false;
}
int hungary(int n) {
int cnt = 0;
memset(rmatch, -1, sizeof(rmatch));
for(int i = 1; i <= n; i++) {
memset(vst, 0, sizeof(vst));
cnt += dfs(i);
}
return cnt;
}
c