图论学习三之Shortest Path最短路

      一些记号


: 由两个集合{V,E}所组成,记作G(V,E)
  • V是图中顶点(Vertex)的非空有限集合
  • E是图中(Edge)的有限集合
  • 子图(subgraph):边的子集,以及相关联的点集

 

 邻接(adjacent):在无向图中,如果边(u,v)E,则uv互为邻接点。
  在有向图中,如果弧<u,v>E,则vu的邻接点。
路径(path):图中一个顶点序列称路径,路上相邻顶点都是邻接的。
  如vv’的路径为(v=V0V1V2Vn=v’),并且
  <V0,V1><V1,V2>…<Vn-1,Vn>集合E
  如果顶点和边都不重复出现,则称为简单路径
  • 除了起点和终点相同外没有重复顶点的路径,称为(cycle)

 

      SPT


从结点s出发的单源最短路构成一棵树(最短路树, SPT

 

      松弛

于一条有向<u,v>
    d[v] = min(d[v], d[u]+w[u][v])
(u,v)上的松弛操作(relaxation)

松弛一条(u,v), 即测试是否可以过起u

目前找到的v的最短路d[v]进行改进

若可以改更新d[v]。 松弛只会减小d[v]


松弛是改变最短路径的唯一方

Dijkstra过程就是通过不断松弛使d[i]敛至dis[i]

 

      关于松弛

 

 


三角不等式对任意边<u,v>E, 有 dis[v] dis[u]+w(u,v)

上界性:算法程中, 对任vV, 有 d[v] dis(s,v)
  而且一旦d[v]达到dis(s,v)值就不再改变(松弛只会减小d[v]) 。

无路径性:若从sv不存在路径, 则总是d[v] = dis(s,v) =

敛性质:如果Ge<u,v>sv的最短路径上, 考察路径suv
  若算法中在松弛<u,v>之前的任何时间d[u] = dis(s,u)则在操
  作过后总d[v] = dis(s,v)

路径松弛性:如果 p = <v0,v1, , vk> 是从S=v0vk的最短路径,
  而且p中的边按<v0,v1><v1,v2><vk-1,vk>顺序进松弛, 那么最
  后d[vk] = dis(s,vk)这个性质的保持并不受其他松弛操作的影响,
  即使他p边上的松弛操作混合在一

 

      最短路径的最优子结构

p = <v1,v2,,vk>v1vk的最短路径, 对于其中的
  任意一段1ijkpij = <vi,,vj>必是v iv j的最短
  路径(反证法)
如果存在一条从s可达的负权回s该回路上的顶点
  之不存在最短路径
最短路不能包含负权回路, 同时也不会包含正权回路。

 

 

Dijkstra——所有边权值非负
Bellman-Ford——许存在负
权边, 但不能有负权回路

 

       Bellman-Ford算法


解决含负权边的带权有向图的单源最短路径问题
  • G无负权回路,则输出最短路
  • G有负权回路,则输出无解
最短路最多只经过(起点不算)N-1个结点,根据路径松弛性质,可
  以通过N-1轮松弛操作得到。

上述算法称Bellman-Ford算法, 它的时间复杂O(VE)

 

      第k轮松弛的结果


k轮松弛之后, 数d [ i ]的意义是什么?
d [ i ]s最多经过k到达i 的最短路径长度
  当然此处假设无负权回路。


算法的最目的是在V-1轮松弛之后, 算出从s
  多经过V-1到达i 的最短路径度。


如果在某松弛之中所有点的d [ i ] 都没有改, 那么
  Bellman-Ford算法就可以提前束了(类似冒泡排序)

 

      判负环


进行V-1轮松弛之后, 再每条加一轮松弛, 如果
  此时有的边仍旧可以被松弛, 意味着G包含s可达的负权
  回 路最短路不存在

 

明:
  ▫ 如果成立, 则说明找到了一条经过n的从uv的路径,
    且其比任何少于n的从uv的路径都短。
  ▫ 一共n顶点, 路径却经过n则必有一个中间顶k
    经过了至少两次。 k是一个回路的起点和点。 走个回
    路比不走个回路路径更短, 只能个回路是负权回路。

 

      Example of BellmanFord

 

      SPFA(Shortest Path Faster Algorithm)


Bellman-Ford算法基础上用队列优化
减少了冗余的松弛操作,是一种高效的最短路算法。

 

维护一个队列,里面存放所有需要进行更新的点。初始时队列中
  只有一个源点Sd[s]=0)。每次取出队头的点u, 尝 试 松 弛 u
  的 所 有 出 边 <u,v> , 若 能 够 松 弛 d[u]+w[u][v]<d[v], 则改进
  d[v]。此时由于s->v的最短距离d[v]变小了,有可能通过v可以改进
  其它结点, 将其push进队。这样一直迭代下去直到队列为空,
  也就是d[i]都确定下来,结束算法。

 

若一个点最短路被改进的次数达到V(结点数)
  • 则说明有负权环(原因同B-F算法)


我们可以用spfa算法判断图有无负权环

 

SPFA算法时间复杂度的上界为O(VE),同Bellman-Ford算法。


记时间为O(kE)
在实际情况下中SPFA表现得非常好, k约等于10

一个好的改
可以用一个布尔数组记录每个点是否处在队列中。 若
u不在队列中(通bool组判断) 才进行push
样保证队列size不会超过结点数V, 因为每个结点出
现一次

 

      Floyd Algorithm


用于求每一对顶点之间的最短路

 

    Dijkstra algorithm


从某个源点s到其余各顶点的最短路径, 即单源最短路径(SingleSource Shortest PathsSSSP)
给定带权图G(V,E)源点s,求从sG中其余各顶点的最短路径。

基本思想:
1. 设置两个顶点的 集合U 集合Q = VUU中存放已经确定最短
  路径(d值) 的顶点, 集合VU存放当前还未确定d的顶点
2. 初始状态时, 集合U中只包含源点S
3. 集合 VU 中选取d值最小的顶点u加入到U中;
4. U中每加入一个顶点u都要更新VU中剩余顶点的d值: dv =
  min{dv, du + w(u,v)}//这一步操作称为“松弛”
5. 重复34,直到集合U中包含全部顶点。

 

      优先队列(priority_queue)


队列(queue):先进先出,队尾入队,队首出队。
优先队列(priority_queue):特别之处在于,允许为队列中元素设置
  优先级(即保持队列是有序的)。元素入队时,根据其优先级插入
  进队列中的相应位置。 STL默认使用“小于 < ”操作符来确定对象之间
  的优先级关系, 所以如果要使用自定义优先级, 需要重载‘ < ’操作
  符

 

      自定义优先级


类似sort中的自定义cmp函数
  struct Node{ //定义Node结构体
    • int d,ind; //
    • friend bool operator < (Node n1, Node n2)
      • { //重载 ’<’ 操作符,使队列按d值升序排列
        • return n1.d > n2.d;
      • }
  • };


对第1种不断更新d值的BFS4处修改:
1. priority_queue<Node> q;//设置优先队列
2. Node cur=q.top();//队首作为cur
3. Node结构体中重载<操作符
4. 当终点Node出队时,可以立即停止并输出d值!

 

      Dijkstra算法的使用条件


下面考虑所有边均为非负的情况。在这种情况下,
最短
路是一定存在的但最长路却不一定存在。


Dijkstra算法可用于计算非负权图上的单源最短路(SSSP)。该算
法同时适用于有向图无向图


结点vd定义为: 当前从源点v0v的最短路径长度(其实
是一个不断被更新的上界)

 

      Dijkstra复杂度


原版: O(N2)
优先队列优化: O(MlogM)


稀疏图(M~N):优化版本O(NlogN) < O(N2)
稠密图(M~N2): 优化版本O(N2logN) > O(N2)

 

 

      最短路算法的选择

 

      需要注意的


注意:单向/双向(无向)图
有没有重边(判负环)和自环
  • 有负环时最短路不存在,有正环时最长路不存在。
注意图是否可能不连通

 

EG:

POJ-1201 Intervals

狼抓兔子

路的最小公倍数

最小密度路径

跳棋

墨墨的等式


如果你不开心,那我就把右边这个帅傻子分享给你吧,
你看,他这么好看,跟个zz一样看着你,你还伤心吗?
真的!这照片盯上他五秒钟就想笑了。
一切都会过去的。
时间时间会给你答案2333
posted @ 2018-07-19 20:21  孟东行#  阅读(448)  评论(0编辑  收藏  举报