最短路

最短路

Floyd 算法

适用于无负环的图。

思路:枚举所有点对 (i,j) 以及中转点 k ,再对邻接矩阵进行松弛操作。

时间复杂度 O(n3) ,可以一次求出任意两点最短路。

bitset 优化传递闭包

用 Floyd 转递闭包时可以用 bitset 优化,时间复杂度 O(n3ω)​ 。

应用

P1119 灾后重建

给出一张无向图,第 i 个点在 ti 时刻被修复,若 ti=0i 未损坏。

q 次询问,每次询问 t 时刻 xy 的最短路,保证给出的 t 不降。

n200

用 Floyd 求最短路,按修复时间枚举中转点松弛即可。

P6175 无向图的最小环问题

给一个正权无向图,找一个最小权值和的环。

枚举中转点 k 时,我们已经得到了前 k1 个点的最短路径。xyykkx 共同构成了环,所以连接起来就得到了一个经过 x,y,k 的最小环。

Bellman–Ford 算法

首先介绍一下松弛操作:disvmin(disv,disu+w(u,v))

该算法不断尝试对图上每一条边进行松弛。由于每次松弛成功都会使得最短路长度增加 1 ,所以循环 n1 次即可求出最短路。

时间复杂度 O(nm)

但还有一种情况,如果从 S 出发,抵达一个负环时,松弛操作会无休止地进行下去。注意到前面的论证中已经说明了,对于最短路存在的图,松弛操作最多只会执行 n1 轮。因此若第 n 轮循环时仍然存在能松弛的边,说明从 S 点出发能够抵达一个负环,但不能说明图中不存在负环。

SPFA 算法

经过队列优化的 Bellman-Ford 算法。

Bellman-Ford 算法中很多点是不用松弛的,只有上一次被松弛的结点所连接的边才有可能引起下一次松弛操作。

于是用队列来维护哪些结点可能会引起松弛操作,就能只访问必要的边了。

若要判负环,则记录一下每个点的松弛次数(即入队次数)即可。

SPFA 算法在随机图上时间复杂度为 O(km) ,但是可以被卡到 O(nm)

一般来说判负环的时候用 dfs 版的 SPFA 更快

优化

比较弱的优化:

  • LLL 优化:使用双端队列,每次将入队结点距离和队内距离平均值比较,如果更大则插入至队尾,否则从队头插入。
  • SLF 优化:使用双端队列,每次将入队结点距离和队首比较,如果更大则插入至队尾,否则从队头插入。

强一点的优化:

  • SLF 带容错:每次将入队结点距离和队首比较,如果比队首大超过一定值则插入至队尾,否则从队头插入。
  • mcfx 优化:定义区间 [l,r] ,当入队点入队次数属于这个区间时从队首插入,否则从队头插入。通常取 [2,n]
  • SLF + swap:每当队列改变时,如果队首距离大于队尾,则交换首尾。

玄学优化:

  • 随机打乱边。
  • 以一定概率从队首/队尾插入。
  • 入队次数一定周期就随机打乱队列。

Dijkstra 算法

将结点分成两个集合:已确定最短路长度的点集 S 的和未确定最短路长度的点集 T

初始时所有的点都属于 T ,令 diss=0 ,其它点的 dis 均为 +

重复操作直到 T 为空:从 T 中选一个 dis 最小的点移到 S 中,并用该点松弛其它点。

Dijkstra 算法只能解决正权图上的最短路问题问题。

具体实现:

  • 暴力:每次暴力找到 dis 最小的点松弛其它点,时间复杂度 O(n2+m)=O(n2)
  • 优先队列:每次松弛 (u,v) 后将 v 插入优先队列中,每次从优先队列中选 dis 最小的点松弛其它点。由于不能在优先队列中删除元素,所以取出时要判重,时间复杂度 O(mlogn)
  • 线段树:基本不用,将上面的操作改为单点修改和全局查询最小值,时间复杂度 O(mlogn)

需要权衡 O(n2)O(mlogn) 两种实现方式的优劣,一般稠密图使用 O(n2) ,稀疏图用 O(mlogn)

O(n2) 的实现:

优先队列优化的 Dijkstra 的实现:

另一种写法(取消了 vis 数组):

应用

P5304 [GXOI/GZOI2019] 旅行者

给定一张有向图和 k 个关键点,求关键点两两之间最短路的最小值。

kn105m5×105

考虑两个关键点 x,y 之间的最短路,先不考虑 x,y 直接连边的情况。记路径上非端点的一个点为 z ,则路径形如 xzy

于是考虑对正图和反图各跑一遍以关键点为源点的最短路,则对于一个非关键点 z ,关键点之间经过 z 的最短路径即为正反两次 disz 的和,对所有 z 的贡献取 min

但是这样是假的,因为可能出现 x=y 的情况。一个简单的想法是跑次短路,但是并不优美。考虑不枚举路径上的点,转而枚举路径上的边,再用两端正反图上起点不同的边更新即可。显然不会枚举到自己到自己的路径,然后可以证明一定会枚举到最短路答案,因此正确性有保证。

给定一张图,每条边 s,t 有两个权值 w,v ,分别表示 (s,t,w)(t,s,v) 的有向边,求边权和最小的包含 1 的简单环。

考虑一个简单环 SxyS ,且 SxyS 不交。为了使答案最优,SxyS 应当在满足无交集的情况下长度最短。先求出 Sx 的最短路 d(x) 以及走的第一个点 p(x) ,再在反图上跑一边相同的流程,记为 d(y)rp(y) 。接下来枚举所有有向边 (u,v,w)

  • u=S :不操作,因为这种情况会被其他情况覆盖。
  • v=S
    • p(u)u :则用 d(u)+w 更新答案。
    • p(u)=u :则 Su 走最短路径不合法,要经过其他的边,这种情况会被其他情况覆盖。
  • 否则:
    • p(u)rp(v) :则用 d(u)+w+rd(v) 更新答案。
    • p(u)=rp(v) :则 SuvS 走最短路径有重叠,也要走其他的路径,这种情况也会被其他情况覆盖。

上面的更新方式可以覆盖所有的简单环情况,所以不会漏过然后一个可能的答案。同时对于经过某条边且可以更新的简单环情况,其长度是最优的。时间复杂度 O(mlogm)

Johnson 全源最短路

如果没有负权边,那直接跑 n 次 Dijkstra 即可。下面考虑怎么处理负权边。

建一个超级源点,所有点与其连一条边权为 0​ 的边。先用 SPFA 求每个点与超级源点的最短路径长度 hi

将每条边 uv 的边权增加 huhv ,最后统计 ij 的最短路时减去 hihj 即可,于是就能直接跑 n 次 Dijkstra 了。

时间复杂度 O(km+nmlogm)

P5905 【模板】Johnson 全源最短路

BFS 相关

在一些特殊的图上,可以用 BFS 求解最短路做到 O(n+m) 的时间复杂度。

  • 无权图上的最短路直接用 BFS 求解即可。
  • 01BFS:若边权仅有 01 ,考虑有 deque 维护 BFS ,若走的边权为 0 则从队首入队,若走的边权为 1 则从队尾入队。

应用

CF173B Chamber of Secrets

一个 n×m 的图,现在有一束激光从左上角往右边射出,每遇到 # ,你可以选择光线往四个方向射出,或者什么都不做。

问最少需要多少个 # 往四个方向射出才能使光线在第 n 行往右边射出。

n,m1000

将柱子改为 # 后,一条光线经过的时候实际效果是该行该列都会有光线。于是视该操作代价为 1 跑 BFS 即可。

次短路

考虑每一条非最短路上的边 uv ,答案即为:

min(dis1,u+w(u,v)+disv,n)

dis1,u,disv,n 建立正反图跑两次 Dijkstra 即可求得。

求严格次短路时,我们不必记录最短路的路径,只需枚举每条边,若路径长度严格小于最短路时更新答案即可。

另一种方式是对于每个点都记录一下最短路与次短路,只要被更新就去松弛别的点。

P2865 [USACO06NOV] Roadblocks G

注意本题求的是严格次短路,下面给出第二种方法的实现。

P1491 集合位置

注意本题选取的第二短路径不会重复经过同一条路,所以只能把最短路上的边依次删去然后跑最短路。

最短路图

即求出所有最短路(多条也算)组成的 DAG,只需将 disv=disu+w 的边连边即可。

P2149 [SDOI2009] Elaxia的路线

给出一张无向图和两对点,求图中两对点间最短路的最长公共路径,注意同一条边走的方向不同也算公共路径。

n1.5×103m3×105

一个显然的事实是最长公共路径一定是连续的一段。

考虑建立 s1t1 的最短路图,注意此时仅保留 dissu+w(u,v)+disvt1 的边即可。

然后在最短路图上拓扑排序,正反各跑一次即可。一个简单的方法是记 fu,gu 表示以 u 为结尾/开始的最长公共路径长度,转移时只要考虑 uv 这条边能不能在 s2t2 的最短路上即可。

「ROI 2017 Day 1」前往大都会

某国有 n 座城市与 m 条单向铁路线,构成一张连通图。第 i 条单向铁路线由 vi,1,vi,2,,vi,si+1 城市组成,城市 vi,j 通过该线路到城市 vi,j+1 花费的时间为 ti,j

1n 花费时间最少的情况下,经过任意两个相邻城市所花费时间的平方和的最大值。

n,m106

首先求出最短路图,那么只要在最短路图上找到平方和最大的路径。

这里的最短路图是 DAG, 于是可以按拓扑序设计 DP 。

dpx 表示以 x 为终点的最大权值,枚举上一个换乘点,有:

fx=maxy{fy+(dxdy)2}=dx2+maxy{dy22dxdy+fy}

斜率优化即可,复杂度瓶颈为最短路。

最短路径树(SPT)

即由最短路径组成的树,和最短路图的区别就是少了几条边。可以通过求解最短路时记录每个点的前驱更新节点求得。

CF545E Paths and Trees

给出一张无向图,给定源点,求边权和最小的 SPT。

n,m3×105

要求边权和最小。可以考虑贪心,在松弛时若遇到松弛前后边权相等时取边权较小者即可。

应用

CF1005F Berland and the Shortest Paths

给出一张无向无边权简单连通图,求 SPT 方案数与方案(若超过 k 种则只取 k 种即可)。

对每个点维护可能成为前驱节点的集合,总方案数就是所有集合大小的乘积,求解方案直接暴力从每个集合中选一个元素组合即可。

P6880 [JOI 2020 Final] オリンピックバス

给定一个有向图,每条边从 ui 指向 vi,经过这条边的代价为 ci ,翻转这条边的代价为 di

可以翻转一条边(或不翻转),求 1n1 的最小代价和。

n200m5×104

考虑求翻转一条边 (u,v)1n 的最短路,n1 是类似的。

一个显然的暴力是暴力每次都跑一次 Dijkstra,时间复杂度 O(mn2)

注意到只有 SPT 上的边才可能影响最短路,其余情况只要用强制经过这条边的最短路与原最短路取较小者即可,所以只要枚举 SPT 上的边即可,时间复杂度 O(n3)

差分约束系统

差分约束系统是一种特殊的 n 元一次不等式组。每个不等式都形如 xixjck ,其中 ck 为常数且 ij 。需要求出一组整数解。

将每个不等式都转化为 xixj+ck ,这与三角形不等式 disvdisu+w 十分相似。那么对于一组不等式 xvxuw ,建边 (u,v,w)

从超级源点向每个点连一条边权为 0 的边,若建图后图中有负环则方程组无解,否则 xi=disi 就是方程组的一组解。

{x1,x2,,xn} 是方程的一组解,则 {x1+d,x2+d,,xn+d} 也是方程的一组解。

tricks:

  • xixj<ck 可以转化为 xixjck1
  • xi=xj 可以转化为 xixj0xjxi0

差分约束系统的一个性质:如果跑的是最短路,则固定一个值时,其余的值都会取到最大值。

v0vu 经过的路径为:

vi1v0l0vi2vi1l1vuviklk

则加起来得到 vuv0l0+l1++lk=dist(0,u)

模板:P3275 [SCOI2011] 糖果

应用

P2474 [SCOI2008] 天平

n 个砝码,分别重 x1n ,给出一些重量大小关系,求有多少对 xa,xb,xc,xd 一定满足:

  • xa+xb>xc+xd
  • xa+xb=xc+xd
  • xa+xb<xc+xd

分别求解三种情况的方案数。

n50

建立差分约束系统后,先跑一边最短路和最长路,求出 i,j 之间的质量差最小值 mni,j 和最大值 mxi,j

转化一下三种情况:

  • xaxc>xbxd ,即 mna,c>mxb,d
  • xaxc=xbxd ,即 mna,c=mxa,c=mnb,d=mxb,d
  • xaxc<xbxd ,即 mna,c<mxb,d

分别统计方案数即可。

[AGC056C] 01 Balanced

构造长度为 n 的字典序最小的 01 字符串,满足 m 组子串 [li,ri] 含相同数量的 01

n106,m2×105 ,保证 rl+1 是偶数

0 当作 11 当作 1 。因为要让答案的字典序最小,即 si 尽可能大,即需要求出字典序最大的一组解,于是可以用差分约束系统求解。

相邻两个位置的限制从 |sisi1|=1 弱化为 |sisi1|1

不可能存在 si1=si :若存在可以构造出 ±1 交错的 s 使得字典序更大。

对于一组限制,转化为 sl1=sr

于是 01bfs 即可求解。

[AGC036D] Negative Cycle

有一张 n 个点的有向图,形如:

  • n1 条形如 ii+1 边权为 0 的边,不可删去。

  • 对于每一对 i<j ,存在边权为 1 的边。

  • 对于每一对 i>j ,存在边权为 1 的边。

删去边 (i,j) 花费 ai,j 的代价,求图中不存在负环的最小删边代价。

n500

考虑差分约束系统,要求图上没有负环,等价于存在一组差分约束的合法解,那么可以把图上的边都写成不等式。

设差分约束系统的合法解为 x1n ,记 x 的差分数组为 ci=xixi+1 ,则:

  • 对于边 ii+1 ,其等价于 xixi+1=ci0

  • 对于边 iji<j ),其等价于 xixj=k=ij1ck1 ,即 [i,j1] 的区间和非 0

  • 对于边 iji>j ),其等价于 xjxi=k=ji1ck1

可以发现,若 ci2 ,则可以调整为 ci=1 ,答案不会变劣,因此仅需考虑 ci{0,1} 的情况。

fi,j 表示考虑到 ici=1 ,上一个 c1 的位置为 j 的最小代价,考虑如何从 fj,k 转移到 fi,j

  • 左右端点都在 [j+1,i] 中的第二类边需要删去。
  • 左端点 [k+1,j] ,右端点 [i+1,n] 的第三类边需要删去。

不难用二维前缀和统计,时间复杂度 O(n2)

同余最短路

同余最短路利用同余来构造一些状态,并将其看作单源最短路中的点。

P3403 跳楼机

给出 x,y,z,h ,求有多少 k[1,h] 满足 ax+by+cz=k

x,y,z105,h2631

不妨设 x<y<z

di 表示仅通过 by+cz 后能得到的模 x 下与 i 同余的最小数,用来计算该同余类满足条件的数个数。可以建边:(i,(i+y)modx,y),(i,(i+z)modx,z) ,于是跑一次最短路即可求出 di

1 作为源点,此时 dis1=1 最小,即可得到最小的一组解,类比差分约束即可得到所有解。

答案即为:

i=0x1(hdix+1)

[ABC077D/ARC077B] Small Multiple

给定一个整数 K。求一个 K 的正整数倍 S,使得 S 的数位累加和最小。

2K105

注意到一个数都可以通过 +1×10 得到。+1 时数位累加和增加,×10 时不变。

因为不需要求出具体数值,输出数位累加和即可,所以我们在 modk 意义下利用同余最短路配合 01BFS 计算即可。

删边最短路

CF1163F Indecisive Taxi Fee

给出一张无向带正权图, q 次询问,每次询问给出 t,x,求若将 t 这条边的长度修改为 x1n 的最短路长度。

n,m,q2×105

首先,若这条边不在最短路上,则答案要么为原来的最短路,要么为经过这条边的最短路,即:

ans=min{dis1,n,dis1,u+x+disv,n,dis1,v+k+disu,n}

否则又分两种情况。若走这条边,答案为 dis1,u+w(u,v)+disv,n

若不走这条边,设删掉这条边后找出的最短路为 E,共有 k 条边分别为 e1k

结论:删掉任意一条边后,一定存在一条 1n 的最短路有一个前缀(可能为空)和 E 重合,有一个后缀(也可能为空)和 E 重合,中间的部分都不在 E 上。

若有两段不在 E 上,因为只删掉了一条边,所以将其中一段换为 E 上的一段一定不劣。

设:

  • lx 表示最小的 i 使得在某条 1x 的最短路上 ei 是第一条 E 上的不在其中的边。
  • rx 表示最大的 i 使得在某条 xn 的最短路上 ei 是最后一条 E 上的不在其中的边。

考虑求 lx,rx 。首先以 1n 为源点分别求一遍最短路,找出一条最短路 E 。对于 E 上的第 i 个点 x,初始化 lx=i,rx=i1

l 为例,r 同理。若边 (u,v) 满足 d1,u+wu,v=d1,v,则 lv=min(lv,lu)。按照 dis1,i 排序后则可以线性更新。注意此时需要满足 1xE 只有一个前缀重合,所以不能用 E 上的边更新。

ai 为删掉 ei 之后的答案。求出 l,r 后枚举不在 E 上的边 (u,v),用 d1,u+wu,v+dv,n 更新 [alu,arv],用 d1,v+wu,v+du,n 更新 [alv,aru]。需要支持区间取 min ,单点查询。因为查询都在修改之后,用 multiset 做一遍扫描线即可。

时间复杂度 O(mlogn+q)

本文作者:wshcl

本文链接:https://www.cnblogs.com/wshcl/p/18297318/ShortestPath

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   wshcl  阅读(44)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开