二分图的定义
二分图是指将图中所有点分为两个集合,任意一条边对应的两个点都不在同一个集合中。
二分图的判定
判定一个图是否为二分图,是考虑这个图中是否有奇数环,如果不存在奇数环,则图为二分图。
具体的判定方法可以使用染色法。
vector<int> g[N]; // vector 存储图
const int N = 100010;
int color[N]; // 标记每个点的颜色,为 0 表示还未被染色
bool dfs(int u, int c) {
color[u] = c;
for (int v: g[u])
if (color[v] == 0) {
if (!dfs(v, 3 - u)) return false;
} else if (color[v] != color[u]) {
return false;
}
return true;
}
// 一边标记为颜色 1,一边标记为颜色 2
bool ans = true;
for (int i = 1; i <= n; ++i)
if (color[i] == 0) {
if (!dfs(i, 1)) {
ans = false;
break;
}
}
if (ans) puts("This graph is biparite graph.");
else puts("This graph is not biparite graph.");
二分图的最大边匹配
对于一个二分图,分为集合 S S S 和 集合 T T T。
选择最多的边,使得这些边对应的点各不相同。
那么解决这个问题的就是匈牙利算法,这也是接下来解决问题的重点算法。
int match[N];
int st[N];
int g[N][N];
bool hungarian(int i) {
for (int j = 1; j <= n; ++j) {
if (g[i][j] && !st[j]) {
st[j] = true;
if (match[j] == 0 || hungarian(match[j])) {
match[j] = i;
return true;
}
}
}
return false;
}
int res = 0;
for (int i = 1; i <= n; ++i)
if (hungarian(i)) res += 1;
printf("二分图的最大边匹配为:%d\n", res);
解释一下上面的几个数组:
-
g[i][j]
是邻接矩阵存储的图 -
match[j]
表示T
中的点j
匹配边的 S S S 中的点。 -
st[j] = true
表示第j
个点已经被其他人考虑了,暂时不能考虑。一个例子:如果有边
1-4
,1-5
,2-4
。对应的集合S = {1, 2}
,集合T = {4, 5}
。首先
S1
与T4
匹配,接着来考虑S2
要匹配的点,S2
只能与T4
匹配,所以考虑T4
目前匹配的点S1
是否可以匹配其他边。幸运的是S1
可以匹配T5
,从而把T4
让出来。但是我们需要考虑在算法的枚举过程中,如果
st[4] == false
,则在使得T4
目前匹配的点S1
考虑其他边时,从小到大,其总会先看到T4
,如此陷入一个死循环。-
一方面限制当前点更换匹配边对应的点不是我们当前选择的这个点
-
另一方面是告诉其他想要拆这个点对应的匹配边的其他点,这里已经考虑过了。如果之前拆分成功,则会一路
return true
退出该函数;如果之前拆分不成功,你这次尝试也不会成功,就别妄想了。
-
二分图的最小点覆盖
对于一个二分图,分为集合 S S S 和 集合 T T T。
选择最少的点,使得其可以覆盖所有的边。(覆盖一条边是指,一条边对应两个点,其中至少有一个点被选择)
一个结论:二分图的最大边匹配 = 二分图的最小点覆盖数,懒得证明了。
二分图的最大独立集与最大团
对于一个图,分为集合 S S S 和 集合 T T T,选择最多的点,但是这些点中任意两个点都不存在一条边,这个点集就是最大独立集。
对于一个图,分为集合 S S S 和 集合 T T T,选择最多的点,使得任意两点之间都存在边,这些点和他们之间的边构成了一个完全图,这个完全图叫作团,选择最多的点满足这个条件,这些点和他们之间的边构成了一个最大团。
在二分图中有一些结论:二分图的最大独立集 = 点数 - 二分图的最小点覆盖。
简单证明下:二分图的最大独立集是指选择最多的点,两两点之间内部不存在边。等价于 “去掉最少的点,将所有边都破坏掉”,即选择最少的点,覆盖所有的边,对应的就是二分图的最小点覆盖。
二分图的最小路径点覆盖和最小路径重复点覆盖
对于一个有向无环图(DAG) ,二分图的最小路径点覆盖是指找到最少的路径,选择的路径之间不存在交点,使得覆盖所有点。
一个结论:二分图的最小路径点覆盖 = 点数 - 二分图的最大边匹配
简单证明:
假设现在有 5 个点,我们建立如下的图,如果从 1 点有一条向 2 点的边,那么我们在下面这个图中从 1 点向 2’ 点连一条边。
对于一个路径来说,其只有一个终点,这个终点没有出边,即对于所有的出点来说,找到所有没有向入点连边的出点,这就是路径数。
故我们需要找到最少的出点,等价于我们需要找到尽可能多的出点都有匹配边,即找到最大边匹配,减去这些有匹配边的出点,剩余的没有匹配边的出点就是路径的终点。
1 1'
2 2'
3 3'
4 4'
5 5'
出点 入点
二分图的最小路径重复点覆盖,可以通过传递闭包,转换为二分图的最小路径点覆盖。
我们现在有路径 x->y->z
,但是 x->y
和 y>z
都已经被走过,那么对于这条路径,剩余价值为 x->z
没走过,将这条边加入到图中。
而传递闭包就是指:x->y & y->z ==> x->z
即 x->y
以及 y->z
可以推导出 x->z
总结
- 二分图等价于:图中不存在奇数环,等价于染色法不会有矛盾
- 匈牙利算法求解二分图最大边匹配
- 二分图的最大边匹配 = 二分图的最小点覆盖 = 二分图总点数 - 二分图的最大独立集 = 点数 - 二分图的最小路径点覆盖