【学习笔记】图论
二分图
一个二分图满足有一种划分方案使得它节点的被分为两部分,且所有边的端点所在的部分不相同。即每条边都连接两个部分。显然我们给二分图染色,确定一个点所有点都确定。如果在染的时候发现冲突就代表不是二分图。
一张图是二分图当且仅当其中间没有奇环。
证明:
-
奇环本身就不能染成二分图。
-
如果没有奇环但此图不是二分图,我们需要找出两条从 \(s\) 到 \(t\) 的路径使得通过这两道路径会让 \(t\) 的颜色产生冲突。
-
很明显就是使这两条路径长度奇偶性不同。
-
因为有两条路径,这两条路径一定形成一个环,因为没有奇环,所以这两个路径长度加起来为偶数,所以这两个路径奇偶性相等。
-
也就是说没有奇环的图一定是二分图。
定义一个边集是二分图的匹配,当且仅当边集中任意两条边没有公共端点。即所有边独立。二分图的最大匹配就是最大的匹配数。
增广路:一个二分图的增广路是相对于一个匹配而言的。一条增广路是由匹配边和未匹配边交替出现,并以未匹配点作为开头结尾的路径。此时将匹配和未匹配边取反会发现匹配数加一。那么这条路径称为增广路。
匈牙利算法:我们尝试一个个加入 a 部分的点。假设在加入第 \(i\) 个点前已经是当前的最大匹配。那么 \(i\) 需要一个个问它连的 b 部分的点,如果有未匹配点就直接连。否则一个个问连的点原本连的点,然后再问……最后当发现问完了也没有自己的位置代表连不上了。听上去很抽象。复杂度的话,每一次都会把记忆化清空,所以复杂度是 \(O(n^2)\) 的。
点覆盖:一个图的点覆盖指一个点集使得每条边至少有一个端点在点集内。显然最大点覆盖就是全部点。
二分图的最小点覆盖就是最大匹配数。
证明:
-
首先最大匹配肯定是点覆盖,无论是不是最小。因为如果有一条边两个端点都没连,那么一定可以选这两个点作匹配,则不满足最大匹配定义。
-
也就是说图中有增广路时,选中的点集一定不能覆盖全图。
-
考虑一条匹配边 \((u,v)\),其中 \(u\) 在左部,\(v\) 在右部。若 \(u,v\) 都能连一个未匹配点,那么会存在增广路。所以假设 \(u\) 能连向未匹配点 \(x\),那么 \(v\) 连向的点一定都是匹配点,所以我们直接把左部或者右部的所有匹配点选了一定满足方案。
-
如果此时少一个点,就代表删掉了这个点的匹配,那么就会出现增广路,则点集一定不会覆盖全图。
-
得证(?)。
最小边覆盖:类比最小点覆盖,就是选择一个尽量小的边集使得所有点都被覆盖。存在边覆盖的前提是没有孤立点。令最小边覆盖为 \(a\),最大边覆盖为 \(b\),则 \(a=n-b\)。
证明(设总共有 \(n\) 个点,\(m\) 条匹配边。):
-
假设我们把所有匹配边都选上,还会剩下 \(n-2m\) 个点未被覆盖。
-
因为一个未匹配点不可能连向一个未匹配点,而有边覆盖的图一定没有孤立点,所以每一个未匹配点都有且仅有连向匹配点的边,所以每一个未匹配点都要且可以通过一条边来连。
-
所以总共要连 \(m+n-2m=n-m\) 条边。
-
选择匹配边的原因是每条匹配边都能覆盖两个点且不重复覆盖,根据贪心思想选择匹配边一定最优。
最大独立集:独立集是指选择一些点集使得这些点之间没有连边。令最大独立集为 \(c\),最小点覆盖为 \(d\),最大匹配数为 \(e\),则 \(c=n-d=n-e\)。
证明:
-
注意到把非独立集中的点删掉会使得这个图没有一条边,也就是说非独立集是一个点覆盖。
-
所以 \(最大独立集=n-最小非独立集=n-最小点覆盖\)。
欧拉图
欧拉图指具有欧拉回路的图。欧拉回路指一条经过图中所有边恰好一次后能够回到起点的路。
半欧拉图指仅具有欧拉通路但不具有欧拉回路的图。欧拉通路指一条能够经过图中所有边恰好一次的路。显然一个图若存在欧拉回路就存在欧拉通路。
欧拉通路一般也叫做欧拉路径,欧拉回路是一种强限制的欧拉路径。
判定欧拉图可以参考小学奥数。
无向图是欧拉图当且仅当有边连接的点连通且每个点度数皆为偶数。
无向图是半欧拉图当且仅当有边连接的点连通且恰好有两个奇度顶点。
有向图是欧拉图当且仅当每个顶点入度和出度相等。
有向图是半欧拉图当且仅当至多一个顶点出度比入度多一,至多一个顶点入度比出度多一,其余顶点入度和出度相等。
求法:遍历当前节点 \(u\) 的所有出边 \((u,v)\),若未走过,那么向节点 \(v\) 搜。遍历完所有出边后,将 \(u\) 加入路径。最终得到的就是一条反着的欧拉路径。倒过来即可。背一下吧不想证明了,应该挺好记的。
一个欧拉图取出 \(n-1\) 条节点的最后一条出边可以得到一棵根向树。相反地也有一个欧拉图的每个点都以一个根向树上的边为最后一条出边时,其他边无论怎么走都可以形成一个欧拉回路。
tarjan
dfs 搜索树(dfs 生成树)
前置知识。
对图中任意一个点开始访问,保留每个点第一次被访问经过的边构造一棵树。剩下的边可以分为三类:返祖边,即某个节点往祖先节点连的边。前向边,即某个节点往子树中非子节点连的边。横插边,即某个节点往与自己没有祖先后代关系的已访问过的节点连的边。
强连通分量
在有向图中,一个强连通分量是一个点集,满足点集中的任意两点互相可达。强连通分量可以用其中任意一个节点表示(类似并查集的表示方法),也可以新给一个编号。
先构建一棵图的 dfs 搜索树。对于搜索树中的节点 \(i\),如果它是它所在的强连通分量在搜索树中访问到的第一个节点,那么其他的节点都在它的子树内,即该节点为深度最低点。可以考虑反证。加入有节点 \(u\) 也在强连通分量中,那么一定有一条路径从 \(i\) 指向 \(u\),但是从 \(i\) 子树出去的边只有可能是返祖边或者横插边,硬性要求通向的节点被访问过,与 \(i\) 是第一个被访问的节点不符。
在一遍 dfs 过程中,我们考虑用栈存被访问过的还没有处理强联通分量的点。同时定义 \(dfn_i\) 表示 \(i\) 的 dfs 序,\(low_i\) 表示在 \(i\) 的子树中,可以回溯到的最早的(即 \(dfn\) 最小的)节点。对于一个强连通分量的第一个点 \(u\),一定有 \(dfn_u=low_u\),因为 \(u\) 为此时的树根。于是若在搜索过程中有 \(dfn_u=low_u\) 我们把 \(u\) 及上面的所有点划作一个强连通分量。
对于一条边 \((x,y)\),现从 \(x\) 访问到 \(y\)。如果 \(y\) 未被访问,代表 \(y\) 处于 \(x\) 子树中,搜索 \(y\),并根据定义让 \(low_x\leftarrow low_y\)。如果 \(y\) 被访问过但是不在栈中了,根据定义代表 \(y\) 的强连通分量已经处理完,无法到达 \(x\),不用管这种情况。如果 \(y\) 被访问过然后在栈中,那么 \(y\) 对于 \(x\) 可达,由于我们并不确定 \(low_y\) 是否仍然在栈中,用 \(dfn_y\) 更新 \(low_x\)。注意这并不会影响正确性,因为如果更新,更新完之后 \(dfn_x\) 一定不等于 \(low_x\),也就不会影响强连通分量的判断。其对其他节点造成的影响同理。
缩点
将有向图中的强连通分量缩成一个点,因为强连通分量中的点互相可达,所以单纯从图的形态表示上是没有问题的。
任何一个有向图缩点后得到的结果都是一个 DAG。
做法就是把所有强连通分量找出来,然后根据原图的连边把不同强连通分量的连边保留即可。
割点
对于无向图来说,割点指将这个点和与这个点相连的边删去后图不连通的点。
仍然考虑 \(dfn_p\) 和 \(low_p\),由于是无向图,所以这里 \(low\) 的定义可以简化为不经过父亲得到的最小时间戳。
对于节点 \(p\),若其儿子中有节点 \(x\) 使得 \(low_x\ge dfn_p\),即无法走到这棵子树外面,就代表 \(p\) 是割点。但是根节点 \(t\) 的 \(dfn_t=1\),任何节点都满足前置条件,此时我们考虑搜索树的性质,若 \(t\) 不是割点,那么 \(t\) 到达的第一个儿子 \(e\) 可以在不经过 \(t\) 的情况下走过其他的所有点,即其他的所有点都无法成为 \(t\) 的儿子,所以若根节点为割点,其一定满足在 dfs 搜索树中至少有两个直接儿子。
割边也叫做桥,在求的时候直接把 \(low_x\ge dfn_p\) 改成 \(low_x>dfn_p\) 就好了,并且没有根节点的特殊判断。
点双
一个图被称作点双,即点双联通图,当且仅当其中所有点都不是割点。
一个无向图中,极大的点双被称为一个点双联通分量,对于极大的定义,就是除自己外没有满足条件的子集包含自己。一个点可能出现在多个不同点双中,但是两个点双一定只有最多一个公共点,此时该点为这两个点双合起来的图的割点,这个显然。对于一个点双,其 \(dfn\) 最小的节点一定是图中的割点或者搜索树的树根。画个图就理解了。
如果这个点是割点,首先此时不能往父亲那边划点,那么割点割的方向的子树中的所有仍然可行的节点归到一个点双,并把除了割点以外的所有点归为不可行。
这一步可以用栈解决。如果这个点是搜索树的根,判断其是否为割点,如果是割点按照割点处理,并且每一个子树都是一个割的方向。如果只有一个子树,可以理解其为点双的树根,按照割点处理。如果没有子树,即为单点,单独算作一个点双。
边双
类似点双,一个边双图不存在割边,类似割边比割点好求,边双也比点双好求,把割边删掉后,每一个连通块为一个边双。感性理解。