二分图的定义

二分图是指将图中所有点分为两个集合,任意一条边对应的两个点都不在同一个集合中。

二分图的判定

判定一个图是否为二分图,是考虑这个图中是否有奇数环,如果不存在奇数环,则图为二分图。 具体的判定方法可以使用染色法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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.");

二分图判定模板题:860. 染色法判定二分图

二分图判定的练习题:257. 关押罪犯

二分图的最大边匹配

对于一个二分图,分为集合 $S$ 和 集合 $T$。

选择最多的边,使得这些边对应的点各不相同。

那么解决这个问题的就是匈牙利算法,这也是接下来解决问题的重点算法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
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$ 中的点。

  • st[j] = true 表示第 j 个点已经被其他人考虑了,暂时不能考虑。

    一个例子:如果有边 1-41-52-4。对应的集合 S = {1, 2},集合 T = {4, 5}

    首先 S1T4 匹配,接着来考虑 S2 要匹配的点,S2 只能与 T4 匹配,所以考虑 T4 目前匹配的点 S1 是否可以匹配其他边。幸运的是 S1 可以匹配 T5 ,从而把 T4 让出来。

    但是我们需要考虑在算法的枚举过程中,如果 st[4] == false ,则在使得 T4 目前匹配的点 S1 考虑其他边时,从小到大,其总会先看到 T4 ,如此陷入一个死循环。

    • 一方面限制当前点更换匹配边对应的点不是我们当前选择的这个点

    • 另一方面是告诉其他想要拆这个点对应的匹配边的其他点,这里已经考虑过了。如果之前拆分成功,则会一路 return true 退出该函数;如果之前拆分不成功,你这次尝试也不会成功,就别妄想了。

二分图最大边匹配的识别:372. 棋盘覆盖

二分图的最小点覆盖

对于一个二分图,分为集合 $S$ 和 集合 $T$。

选择最少的点,使得其可以覆盖所有的边。(覆盖一条边是指,一条边对应两个点,其中至少有一个点被选择)

一个结论:二分图的最大边匹配 = 二分图的最小点覆盖数,懒得证明了。

二分图最小点覆盖的识别:376. 机器任务

二分图的最大独立集与最大团

对于一个图,分为集合 $S$ 和 集合 $T$,选择最多的点,但是这些点中任意两个点都不存在一条边,这个点集就是最大独立集。

对于一个图,分为集合 $S$ 和 集合 $T$,选择最多的点,使得任意两点之间都存在边,这些点和他们之间的边构成了一个完全图,这个完全图叫作团,选择最多的点满足这个条件,这些点和他们之间的边构成了一个最大团。

在二分图中有一些结论:二分图的最大独立集 = 点数 - 二分图的最小点覆盖。

简单证明下:二分图的最大独立集是指选择最多的点,两两点之间内部不存在边。等价于 “去掉最少的点,将所有边都破坏掉”,即选择最少的点,覆盖所有的边,对应的就是二分图的最小点覆盖。

二分图的最大独立集识别:378. 骑士放置

二分图的最小路径点覆盖和最小路径重复点覆盖

对于一个有向无环图(DAG) ,二分图的最小路径点覆盖是指找到最少的路径,选择的路径之间不存在交点,使得覆盖所有点。

一个结论:二分图的最小路径点覆盖 = 点数 - 二分图的最大边匹配

简单证明:

假设现在有 5 个点,我们建立如下的图,如果从 1 点有一条向 2 点的边,那么我们在下面这个图中从 1 点向 2’ 点连一条边。 对于一个路径来说,其只有一个终点,这个终点没有出边,即对于所有的出点来说,找到所有没有向入点连边的出点,这就是路径数。 故我们需要找到最少的出点,等价于我们需要找到尽可能多的出点都有匹配边,即找到最大边匹配,减去这些有匹配边的出点,剩余的没有匹配边的出点就是路径的终点。

1
2
3
4
5
6
1       1'
2       2'
3       3'
4       4'
5       5'
出点    入点

二分图的最小路径重复点覆盖,可以通过传递闭包,转换为二分图的最小路径点覆盖。 我们现在有路径 x->y->z,但是 x->yy>z 都已经被走过,那么对于这条路径,剩余价值为 x->z 没走过,将这条边加入到图中。 而传递闭包就是指:x->y & y->z ==> x->zx->y 以及 y->z 可以推导出 x->z

二分图的最小路径重复点覆盖、二分图的最小路径点覆盖识别

总结

  1. 二分图等价于:图中不存在奇数环,等价于染色法不会有矛盾
  2. 匈牙利算法求解二分图最大边匹配
  3. 二分图的最大边匹配 = 二分图的最小点覆盖 = 二分图总点数 - 二分图的最大独立集 = 点数 - 二分图的最小路径点覆盖