总结:图论算法

一篇比较奇怪的总结,记录一下乱七八糟的图论相关内容。暂时就这么点,以后看到有趣的东西再补充吧。

听大爷们说下面记的东西都很简单,可是我觉得很有趣,emm,所以就当我记着玩好啦。

可达性类#

O(n2ω) 强连通分量#

Tarjan 算法由于自身算法原理(需要找到 DFS 树上的每一条返祖边)的缘故而难以压位,考虑 Kosaraju 算法。

K 算法的框架就是普通的两遍 DFS,把未访问过的节点压位存起来,DFS 树上回溯时更新这个点集即可做到这个复杂度。

该算法在一些奇怪的题目里比较好用,比如点数不多但是需要多次动态求连通性的情况。

例题:SLYZOJ #76,不过现在校内 OJ 还没通外网,所以暂时只有一中机房能看

补图上做 BFS#

模型:给定一张图,要求在其补图上维护可达性相关信息。

做法很多,我习惯的做法是用一个队列储存所有未访问节点,每次查询当前节点与队列中所有节点之间是否有连边,把没有连边的点再丢回去。

一个点被丢回队列里等价于找到了一条不存在的边,因此复杂度是正确的。

经典题:CF920E

例题:CF1508C

最短路类#

Dijkstra 的另一种理解方式#

杜爷如是说

在求解单源最短路问题时,我们将点和边都看成待求解的对象。设到一条边的最短路为其两顶点最短路的较小值加上自身边权。

每次从堆中取堆顶,如果取出来的是一条边,那么更新其端点的最短路;如果是一个点,更新其所有出边。

这样一来,由堆优化 Dijkstra 的自身性质我们可以发现,每条边的最短路只会被更新一次,每个点的最短路也只会被更新一次。

通常我们写的 Dijkstra 算法是将边与点合并起来的,这样的话每个点会多次被插入到堆中,这是因为边被省略了,与这个点相连的每条边都有可能会更新这个点的最短路。

这种理解方式自有其妙处:在原图边数较多但有一定分布规律的情况下,搭配任意一种势能分析的数据结构,复杂度可以得到极大的优化(因为每条边 / 点的最短路只被更新一次,更新过后这个元素可以被直接删掉,这种性质天然具有势能分析的优势)。不过好像在很多需要用到这类性质的题目中边还是被简化掉的(因为更新边的同时可以顺便更新端点)。

例题:LOJ #3282(洛谷有很多 JOI 题的翻译都比较奇怪,我还是喜欢 LOJ 的翻译风格)

只有常数种边权的最短路#

用人话说就是边权种类很少。对每一种边权开一个队列,再对所有的队首元素建一个堆。每次取出堆顶的同时更新队首元素。

这个做法的正确性建立在队列中元素最短路单调的前提下,这一点不难证明(按照上面说的理解方式把边看作元素,Dijkstra 的过程中取出的元素最短路长度单调)。

这个算法其实还是模拟了 Dijkstra 的流程,在已知边权种类很少的情况下常数要优秀一些。

生成树类#

这类好像有很多各种各样的奇怪算法,大多数我都不会,随便记几个我会的。

斯坦纳树#

默认是最小斯坦纳树。原图上有几个关键点,求一棵包含所有关键点的树,最小化总边权。

斯坦纳树不要求包含所有点,因此严格来说或许不能算是生成树……?

对于点数很多,需要求解次数比较少的情况,设 f(i,S) 表示以 i 为根,已经包含的关键点集合为 S 时的最小边权和。转移考虑两个关键点集合的合并和换根,前者枚举子集即可,后者需要跑最短路。

一个有趣的现象是,在网格图上求斯坦纳树时,Bellman-fordDijkstra 算法优秀一点。我也不知道为什么,之前有一场正睿类似题目就被复杂度不对的斯坦纳树做法艹翻了,所有卡过去的老哥都是用的 Bellman-ford,并且卡不掉。

但如果点数不多,关键点数量很多的话,这个做法时间复杂度是 O(3nn+2nn2),空间复杂度是 O(n2n),都不可接受。有时候还需要对多个点集求斯坦纳树。

此时有个很傻逼的做法,对每个点集状压求最小生成树,然后向子集转移。时间复杂度 O(n2n),空间复杂度 O(2n),都可以接受。

最短路径树与 BFS 树#

求最短路过程中记录转移边形成的结构。

多源 BFS 树#

给定多个源点,求一棵树,满足每个点到任一源点的最短路都等于树上距离。

这个东西存在的必要条件之一是任意两个源点的最短路唯一(而且似乎必须构成一棵树状物)。除了位于任意两个源点最短路径上的点,其他所有点还都必须能找到至少一个符合每个源点最短路树性质的父节点。

模板题:CF1495D

最小直径生成树(图的绝对中心)#

直接放模板题:CF266D,苏铁的题解讲得很明白啦。苏铁太强啦!

图的绝对中心指的是某个位置(可能是在顶点处或边上),满足其到所有顶点的最大距离最小。

顶点的情况是好处理的。下面考虑边。

对于一条边,在一个源点从 uv 移动的过程中,把图上每个顶点到该点的最短路变化看作函数,那么这个函数取的是两个斜率固定的一次函数的较小值。

我们把 u,v 到图上每个点的距离分别排序,就可以双指针找出这些函数的全部交点(因为斜率固定)。在交点处更新答案即可。

得到绝对中心之后建最短路树,我们就得到了原图的最小直径生成树。

posted @   LFCode  阅读(566)  评论(10编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示
主题色彩