Appearance
二分图是一种特殊的图结构。
如果图 G 可以划分成两个点集
判定
图 G 是 2- 着色的 图 G 是二分图。
容易证明,将点属于的集合
dfs 朴素染色即可,若出现相邻节点颜色相同则图不是二分图,因为每个点只会被染一次,所以时间复杂度:
cpp
bool dfs(int x, int c) {
col[x] = c;
for (auto u : p[x]) {
if (!col[u]) {
if (!dfs(u, 3 - c))
return 0;
} else if (col[u] == c)
return 0;
}
return 1;
}
for (int i = 1; i <= n; i++) {
if (!col[i]) {
if (!dfs(i, 1)) {
cout << "No\n";
return;
}
}
}图 G 中不存在奇环 图 G 是二分图。
容易发现,奇环无法二染色,偶环可以二染色,环只有奇环和偶环,去掉环后,图是一棵树,树一定可以二染色。
考虑怎么在图上表示一个环,建出图的 dfs 生成树(容易发现二分图在各个连通块之间独立,所以这里仅考虑一个连通块)那么环就是树上一条路径加一条若干条非树边。
若将仅一条非树边形成环定义成“基环”。
那么容易发现,一般环可以在 dfs 生成树上对应成若干基环。
若每一个基环都是偶环,那么形成的每一个生成环,也都是偶环。
所以只要判掉是否存在一个奇长的基环即可。树上链长可以使用节点深度作差得到。
时间复杂度:
cpp
// 维护 dfs 生成树
for (int i = 1; i <= n; i++) {
for (auto u : p[i]) {
if (u != fa[i] || fa[u] != i) {
if ((dep[u] & 1) == (dep[i] & 1)) {
// 存在奇环
return;
}
}
}
}扩展域并查集判二分图
从二分图定义出发,点集需要能被划分成两个集合。
那么对于一条边
使用扩展域并查集维护即可。
时间复杂度:
cpp
for (auto [u, v] : edge) {
if (dsu.same(u, v) || dsu.same(u + n, v + n)) {
// 不能划分到两个集合中
return;
}
dsu.merge(u, v + n);
dsu.merge(u + n, v);
}使用扩展域并查集可以在线判断二分图。
应用
基于二分图的特殊性质,一些在一般图上比较困难的问题会变得相对容易。
1. 二分图最大匹配
匹配指一组边,图中每个点只出现在其中一条边中。
最大匹配指要求选出最多边。
求解:
一般情况下,使用匈牙利算法即可求二分图的最大匹配,时间复杂度:
注:极端情况下,二分图使用网络流算法 Dinic 求最大流,时间复杂度为
构造:
匈牙利算法可得最大匹配的一种可行方案。
2. 二分图最小点覆盖
最小点覆盖问题是指,在一张无向图中选择最少的顶点,满足每条边至少有一个端点被选。
求解:
一般图的最小点覆盖问题是 NP-hard 的,在二分图上,最小点覆盖
构造:
和最大匹配相同,选择最大匹配的每条边的一个顶点即可。
3.二分图最大独立集
最大独立集问题是指,在一张无向图中选择最多的顶点,满足两两之间互不相邻。
求解:
最大独立集问题对于一般图是 NP-hard 的,在二分图上,最大独立集
构造:
取最小点覆盖补集。
4.有向无环图最小路径覆盖
最小路径覆盖问题是指,在一张有向图中,选择最少数量的简单路径,使得所有顶点都恰好出现在一条路径中。
一般的有向图上的最小路径覆盖问题是 NP-hard 的。
求解:
DAG 的最小路径覆盖
构造:
5.有向无环图最小路径重复点覆盖
最小路径覆盖问题是指,在一张有向图中,选择最少数量的简单路径,覆盖所有点,点可以出现在多条路径上。
求解:
记原图
一个简单转换:在 DAG 中,若
DAG 上可达是
构造:
6. 二分图最大权匹配
二分图的最大权匹配是指二分图中边权和最大的匹配(相应地,最大匹配相当于边权为
求解:
KM 算法可以在
