算法学习笔记(7): 二分图

算法,学习,笔记,二分 · 浏览次数 : 54

小编点评

**二分图目录二分图二分图的最大匹配匈牙利算法(增广路算法)增广路定义** **二分图的覆盖于独立集关系** 二分图的覆盖于独立集是指在一个图中,如果一个点可以被分成两个互斥的集合,那么该点所在的集合必须是独立集。 **二分图的最大匹配** 二分图的最大匹配是指在一个图中选择尽量多的边,使得每一个点至多被一条边覆盖或说:二分图的一组匹配 \\(S\\) 是最大匹配,当且仅当图中不存在 \\(S\\) 的增广路匈牙利算法这是一种不常用的算法增广路定义对于任意一组匹配 \\(S\\) (一个边集), 属于 \\(S\\) 的边被称为“匹配边“,不属于 \\(S\\) 的边称为”非匹配边“。 **增广路算法** 增广路算法是一种在二分图中寻找最大匹配的方法。该算法使用了匈牙利算法的增广路定义来选择尽量多的边,使得每一个点至多被一条边覆盖。 **增广路的流程** 增广路算法的流程如下: 1. 遍历所有点,为每一个点寻找一个匹配点。 2. 对于每一个匹配点,尝试更换该点 的匹配点。 3. 每次贪心最多只会遍历图一次,为\\(O(n)\\) 次重复上述步骤,使得所有点都被遍历到算法的正确性。 **时间复杂度** 增广路算法的时间复杂度为 \\(O(NM)\\),其中 \\(N\\) 是图中点的数量, \\(M\\) 是图中边的数量。

正文

二分图

Bipartite graph, 又称二部图

定义:如果一张无向图的\(N\)个节点可以分成两个没有相同点的非空集合\(A\), \(B\),且存在一种分法使得同一个集合内的点没有相连的边,那么这个图为二分图\(A\), \(B\), 分别为此二分图的左部和右部。

判定

定理:一个图是二分图当且仅当图中不存在奇环(长度为奇数的环)

更具该定理,我们可以通过染色法进行二分图判定:

  • 尝试用两种颜色标记节点,如果有一个点的染色产生了冲突,那么这个图就不是二分图

  • 染色结束之后,\(A\), \(B\)集合中的点分别为黑色点集以及白色点集

复杂度\(O(N +M)\)

二分图的最大匹配

在二分图中选择尽量多的边,使得每一个点至多被一条边覆盖

或者说:二分图的一组匹配 \(S\) 是最大匹配,当且仅当图中不存在 \(S\) 的增广路

匈牙利算法(增广路算法)

这是一种不常用的算法

增广路定义

对于任意一组匹配 \(S\) (一个边集), 属于 \(S\) 的边被称为“匹配边“,不属于 \(S\) 的边称为”非匹配边“,匹配边的端点为“匹配点”,其他节点为“非匹配点”。如果二分图中存在一条连接两个非匹配点的路径,使得“匹配边”和“非匹配边”交替出现,那么称这条路径为增广路,也称交错路

算法流程

  1. \(S = \empty\) ,即所有边都是非匹配边
  2. 寻找一条足够长的增广路,把路径上的所有状态取反:匹配边变为非匹配边,非匹配边变为匹配边
  3. 重复上述步骤,直到图中不存在增广路

听着很抽象,我们换一种讲法

总流程:遍历所有点,为每一个点寻找一个匹配点,两点互相匹配,且一个点有且只有一个匹配

  1. 遍历每一个结点

  2. 尝试当前结点 \(s\) 和一个点 \(t\) 匹配,如果 \(t\) 以及被匹配,尝试更换 \(t\) 的匹配。注意:在最外层遍历时,每一次每一个点只能被匹配一次,总共最多会匹配 \(O(n)\)

  3. 重复上述步骤,使得所有点都被遍历到

算法的正确性基于能反悔的贪心策略。每一次贪心最多只会遍历图一次,为\(O(M)\),所以,算法的时间复杂度为 \(O(NM)\)

下面给出一种参考:

int match[N], vis[N], vt = 0;
bool dfs(int x) {
    // 采用链式前向星的写法
    for (int y, i = head[x]; i; i = edge[i].next)
        // 在每一次遍历中,一个点只会访问一次
        if (!vis[(y = edge[i].to)]) {
            vis[y] = vt;
            if (!match[y] || dfs(match[y]) {
                match[y] = x; return true;
            }
        }
    return false;
}

int main() {
    // ....

    int matchCount = 0;
    // vt: visit time, 用于避免memset的使用
    for (vt = 1; vt <= n; ++vt) {
        if (dfs(vt)) ++matchCount;
    }

    // ....
    return 0;
}

值得一提的是,如果要寻找最大二分匹配,不仅仅可以使用最简单的匈牙利算法,使用网络流也可以做到类似的效果,并且效率更高。这一部分我放在网络流部分讲述

二分图的覆盖于独立集

下面给出的定义会非常的迷惑,请仔细阅读于理解

  • 最小边覆盖:你可以认为是用最少的边覆盖所有的点。选择尽量少的边,使得任意一个点相连的边中至少有一条边被选中

  • 最小点覆盖: 同理,用最少的点覆盖所有的边。选择尽量少的点,使得任意一条边的两个端点至少有一个被选中

  • 最大点独立集:在不重复覆盖一条边的情况下,选择最多的点。选择尽量多的边,使得任意一条边的两端至多有一个点被选

  • 最大边独立集:在不重复覆盖一个点的情况下选择最多的边。这不就是最大二分匹配?

    • DAG的最小路径点覆盖:感觉没啥用。用最少的路径,不重复的覆盖所有的点

关系

其实这四者是有数量关系的,由于证明相对复杂,可以参考《算法竞赛进阶指南》图论二分图部分

总而言之就是:

graph LR; A[A最大匹配] B[B最大边独立集] C[C最大点独立集] D[D最小点覆盖] E[E最小边覆盖] A ----|等价| B B ----|和为N| E B ----|相等| D B ----|和为N| C C ----|和为N| D C ----|相等| E D ----|和为N| E

放弃用mermaid挣扎,还是画图好用

关系

参考题目

  • CH6801
  • CH6901
  • CH6902
  • POJ1325
  • POJ2226
  • ZJOI2007 矩阵游戏
  • NOIP2010 关押罪犯

与算法学习笔记(7): 二分图相似的内容:

算法学习笔记(7): 二分图

# 二分图 [TOC] > Bipartite graph, 又称二部图 **定义**:如果一张无向图的$N$个节点可以分成两个没有相同点的非空集合$A$, $B$,且存在一种分法使得同一个集合内的点没有相连的边,那么这个图为**二分图**,$A$, $B$, 分别为此二分图的左部和右部。 **判定

VisionPro学习笔记(7)——FitLineTool

如果需要了解其他图像处理的文章,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice VisionPro有很多的示例和算子,这里再展示一个比较好用的算子FitLine Tool。我自己

算法学习笔记(6): 树链剖分

树链剖分 树链剖分是一个很神奇,但是在树上可以完成一些区间操作问题 简单来说,就是把一棵树分成一条条的链,通过维护链上的信息来维护整棵树的信息 基础知识可以参考我的另外一篇博客:算法学习笔记(5): 最近公共祖先(LCA) 这里假设你已经掌握了上述博客中的所有相关知识,并清晰了其背后的原理 性质?发

算法学习笔记(11): 原根

原根 此文相对困难,请读者酌情食用 在定义原根之前,我们先定义其他的一点东西 阶 通俗一点来说,对于 $a$ 在模 $p$ 意义下的阶就是 $a^x \equiv 1 \pmod p$ 的最小正整数解 $x$ 或者说,$a$ 在模 $p$ 意义下生成子群的阶(群的大小) 再或者说,是 $a$ 在模

算法学习笔记(30):Kruskal 重构树

Kruskal 重构树 这是一种用于处理与最大/最小边权相关的一个数据结构。 其与 kruskal 做最小生成树的过程是类似的,我们考虑其过程: 按边权排序,利用并查集维护连通性,进行合并。 如果我们在合并时,新建一个节点,其权值为当前处理的边的权值,并将合并的两个节点都连向新建的节点,那么就可以得

算法学习笔记(3.1): ST算法

ST表 在RMQ(区间最值)问题中,著名的ST算法就是倍增的产物。ST算法可以在 \(O(n \log n)\) 的时间复杂度能预处理后,以 \(O(1)\) 的复杂度在线回答区间 [l, r] 内的最值。 当然,ST表不支持动态修改,如果需要动态修改,线段树是一种良好的解决方案,是 \(O(n)\

C++算法之旅、09 力扣篇 | 常见面试笔试题(上)算法小白专用

算法学习笔记,记录容易忘记的知识点和难题。详解时空复杂度、50道常见面试笔试题,包括数组、单链表、栈、队列、字符串、哈希表、二叉树、递归、迭代、分治类型题目,均带思路与C++题解

C++算法之旅、08 基础篇 | 质数、约数

算法学习笔记,记录容易忘记的知识点和难题。试除法、分解质因数、筛质数、约数个数、约数之和、最大公约数

算法学习笔记(1): 欧几里得算法及其扩展

扩展欧几里得算法详解 在了解扩欧之前我们应该先了解欧几里得算法 欧几里得算法 这是一个递归求最大公约数(greatest common divisor)的方法 $$ gcd(a, b) = gcd(b, a % b) $$ 可以通过一个类似的简单公式推导而来 好像叫做辗转相减法来着? $$ gcd(

算法学习笔记(3): 倍增与ST算法

倍增 目录倍增查找 洛谷P2249重点变式练习快速幂ST表扩展 - 运算扩展 - 区间变式答案倍增更多的用法优化矩形查询优化建图优化 DP作者有话说 倍增,字面意思即”成倍增长“ 他与二分十分类似,都是基于”2“的划分思想 那么具体是怎么样,我们以一个例子来看 查找 洛谷P2249 依据题面,我们知