原来做第k短路用的是A*,但是后来发现A*对于k特别大的时候不好用,就去学了一种更高级的算法
学习资料:俞鼎力写的第k短路和堆的可持久化
方法:
对于图G,建立一个以终点t为起点的最短路径构成的最短路径树(就是反着跑一遍最短路,然后对于一个不为终点的点v,v到终点t的最短路径上(任选一条)v的后继结点为v的父亲,就形成了一棵树),然后对于所有点,定义其不在最短路径树上的出边的f值为:f[e] = l[e] + dis[e.tail] - dis[e.head] ,就是走这条边,走到t需要多绕的距离,那么我们只要找到第k小的这种边的序列就得到解了。
那么我们维护按权值一个从小到大的优先队列,每次从队头取出一个序列q,设q的最后一条边e的head为u,tail为v,我们可以选择在序列的末尾加上v到t的所有路径上非树边的最小的得到一个新的序列q1,或者选择u到t的所有路径上所有非树边中e的后继(没用过的边中最小的)替换e得到q2,将q1,q2都塞进优先队列,重复k次,(论文中说是k次,我个人实现是k-2次,可能方法有一些不一样)。
可是怎么才能尽快知道一个节点v到t的所有路径上的非树边最小的一个呢?打个可持久化的可并堆就没问题了,每个点合并他到根的路径上所有非树出边,然后对于找e的后继替换e的操作,直接找e的两个孩子就行了
(重边自环好像都是不需要特判的,但是记得对于不能到达终点t的边要删掉)
时间复杂度是O( n log n + m log m + k log k )
关于例题(写的好丑)
code:
#include