图论其他算法

题单:点我

二分图

……

k 短路

原理

k 短路问题一般使用 A* 算法解决,由于可持久化可并堆优化难度过高(黑题),本文将不会涉及相关内容。

A*,即启发式 bfs,依赖以下函数(设起点 \(s\),终点 \(t\)

  • \(g(x)\),代表按照 \(x\) 状态的路径走,从 \(s\)\(x\) 所在点的实际代价
  • \(h(x)\),代表 \(x\) 所在点到 \(t\)估计代价
  • \(h^*(x)\),代表 \(x\) 所在点到 \(t\)实际代价
  • \(f(x)=g(x)+h(x)\),为估价函数

注意,本文中状态并非简单的一个点,而包含了该状态所在的点和从起点来该点的路径。路径信息可以直接通过存储 \(g(x)\) 代替,因为路径并不重要,重要的是路径长度。

初始时,以 \(s\) 构造状态,放入堆中。每次取出估价函数 \(f(x)\) 最小的状态扩展子状态,放入堆中。当取出的状态到达 \(t\) 时,即得到了最优解。

一般地,为了得到最优解,要求 \(\forall x,h(x)\le h^*(x)\),即估价函数必须乐观。为什么呢?设 \(d\) 为最优解(最短路长度),

  • 对于最优路径上的任意状态 \(x\)\(d=g(x)+h^*(x)\ge g(x)+h(x)=f(x)\),即有 \(f(x)\le d\)
  • 而对于非最优路径上的任意状态 \(y\),必然存在 \(y\) 的子状态 \(z\),使得 \(f(z)=g(z)+h(z)>d\)。考虑状态 \(z\) 表示 \(y\) 的子状态,且 \(z\) 到达了 \(t\)(或到达 \(t\) 附近)。此时 \(f(z)=g(z)+h(z)>d+h(z)\),正常的有 \(h(z)=0\),故 \(f(z)>d\)

也就是说,即使 \(z\) 被压入堆,也不会被取出,而会取出 \(x\)

总之,只要 \(f(x)\) 乐观,就可以得到最优解。如果悲观,则可以快速求出没那么优的解。当然,如果 \(\forall x,h(x)=h^*(x)\),即可最快地稳定地求出最优解。估价函数应尽量接近实际代价。

类似的,可以证明到达每个点的第 \(k\) 小代价为 在该点的第 \(k\) 个出堆的状态的代价。人话:如果一个状态第 \(k\) 个到达某个点,它即为 \(k\) 短路。

那么怎么设计估价函数呢?

考虑在反向图上跑单源最短路,起点为 \(t\)。设 \(u\)\(t\) 的最短路为 \(dis(u,t)\),估价函数即为 \(f(x)=g(x)+h(x)=g(x)+dis(x,t)\)。这里 \(dis(x,t)\)\(x\) 实际上是 \(x\) 所在的点。也就是说,我们以到 \(t\) 的最短路作为 \(h(x)\)。事实上,dijkstra 实现单源最短路本质也是 A*,其中 \(h(x)\equiv 0\)

实现

差分约束

引入

考虑单源最短路的一个性质:

在有向图中,若存在边 \(u\to v\),边权为 \(w\),则 \(dis_u+w\ge dis_v\)

正确性是显然的,因为如果反之 \({dis}_u+w<dis_v\),我们就可以令 \({dis}_v\gets dis_u+w\),原先的就不是最短路了,与题设矛盾。

那么怎么使用这个性质呢?

正文

当题目中出现形如

\(m\) 个限制条件,\(n\) 个变量 \(x_i\),每个限制条件形如:\(x_a-x_b\le y\)

时,就可以建图跑最短路。\(x_a-x_b\le y\implies x_b+y\ge x_a\),即 \(b\)\(a\) 连边权为 \(y\) 的边。

例题

Luogu P5960 【模板】差分约束

直接连边跑最短路即可。但观察到无法快速确定一个源点,使得从它开始可以遍历到每个点。

技巧 \(1\):可以建立超级源点 \(0\),让 \(0\) 向每一个点连边。如果被卡可以 random_shuffle() 一下 \(0\) 的出边。

Luogu P3275 [SCOI2011] 糖果

依然考虑差分约束。

先使用技巧 \(1\),超级源点连所有点,边权为 \(1\),这样满足每个小朋友都可以分到糖果。

注意到边权只为 \(\{0,-1\}\),跑最短路可能使小朋友倒贴糖果 \(dis\) 为负。

当然,可以一负到底,将上面连超级源点的边权改成 \(-1\),然后输出 \(-dis(i)\) 即可。

技巧 \(2\):变正权边。

可以将边权取相反数,跑最长路。注意该技巧没有本质优化,不可以实现“变正权边跑 dijkstra”之类的操作。

这个技巧唯一的用处就是思路更顺。注意即使是“最长路”求出的未知数和照样最小,最长路只是保证约束条件。

但本题可以把队列优化 Bellman-Ford 卡爆!怎么办?

技巧 \(3\):配合 Tarjan 缩点。

以下题解使用了 技巧 \(2\),当然不用也可以做。

先缩点,每个 SCC 内部如果出现了一条 \(u\)\(v\) 的边权为 \(1\),根据 SCC 的定义,一定还存在一条 \(v\)\(u\) 的路径,由于边权 \(\ge 0\),所以一定会出现一个正权环,则这个差分约束系统无解。

否则的话,发现 SCC 内部变量取值一定是相同的,那么问题变成了一个 DAG 上最长路,拓扑排序即可。

——来自该题题解

Luogu P3530 [POI2012] FES-Festival

题意:\(x_a=x_b-1,x_c\le x_d\)。求 \(|\{x_1,x_2,\dots,x_n\}|\) 的最大值(即最大化 \(x\) 中的数的种数)。

考虑转化题意

  • \(x_a=x_b-1\iff x_a\ge x_b-1\land x_a\le x_b-1\iff (a,b,1),(b,a,-1)\)
  • \(x_c\le x_d\iff (d,c,0)\)

首先要明确:\((a,b,w),(b,c,w')\implies (a,c,w+w')\),因为 \(dis(a)+w+w'\ge dis(b)+w'\ge dis(c)\)。即 \(dis(b)-dis(a)\le w+w'\)。更一般地,可以推广到任意条边。

考虑对于一般的差分约束系统,点与点的连通性非常混乱,难以出现很强的约束关系。考虑在强连通分量中,其中任意点 \(u,v\),显然有 \(x_v-x_u\le dis(u,v)\),同时 \(x_u-x_v\le dis(v,u)\),因为 \(u,v\) 双向可达。于是我们得到 \(-dis(u,v)\le x_u-x_v\le dis(v,u)\)。也就是说,\(x_u-x_v\) 的差有一个固定的范围。

考虑在数轴上的意义。一个强连通分量 \(S\) 中的点的未知数在数轴上必然有最大值和最小值,它们的差(极差)就是 SCC 内任意两点间最短路的最大值。因为 \(\max x_u-x_v\le\max dis(v,u)\),可以取等。于是我们得到了 SCC 内极差。

考虑 SCC 内最多取值数(SCC 内答案)。由于边权只取 \(\{-1,0,1\}\),显然有 \(\forall u,v\in S,dis(u,v)<|S|\)。所以 \(\max x_u-x_v<|S|\),也就是说数轴上 \([x_u,x_v]\) 的整点会被填得满满的。即,SCC 内答案为 \(\max x_u-x_v=\max dis(v,u)\)

考虑原图。缩点后,图变成 DAG,且显然剩下的边边权为 \(0\)(第二种限制),反之可以继续缩点。也就是说,只剩下 SCC 间的 \(\le\) 关系。于是,我们可以令任意两个 SCC 在数轴上的距离无限拉大,这样各个 SCC 间就互相独立了。累加各个 SCC 的答案即为最终答案。

posted @ 2024-06-30 11:48  Po7ed  阅读(1)  评论(0编辑  收藏  举报