首页 > 其他分享 >图的最短路

图的最短路

时间:2023-03-31 18:24:01浏览次数:33  
标签:队列 短路 源点 BFS 闹钟 我们

图的广度优先搜索

在DFS中,一旦发现一个新节点就会立即执行从它开始的递归,这个算法一开始执行就会离源点越来越远,因此称为“深度优先”。这种搜索方式与“栈”后进先出的特性是相同的,我们甚至可以避免递归而用“栈”来实现图的深度优先搜索。

与“后进先出”的栈相对应是“先进先出”的“队列”。把源点推入队列,在队尾推入与源点相连的点,接着我们将队头(此时是源点)从队头推出,然后从队头开始取出结点,每次再把与当前节点相邻且没有访问过的点推入队尾……这种搜索方式称为“广度优先搜索”(BFS)。容易发现,BFS的复杂度也是线性的。

我们尝试用BFS来访问没有边权(边权为1)的图。起初队列里只有源点。之后某个时刻队列中加入了所有与源点距离为1的点,并且弹出了源点本身——此时队列里有且仅有所有到源点距离为1的点。接着我们会依次访问所有这些距离为1的点,并且推入所有与这些点相邻的点。由于所有距离为1的都已经在队列里了,因此所有这些后加入的点到源点的最短距离至少为2。而容易看出2对于这些点来说都是可行的,因此我们证明了接下来被推入的节点到源点的最短距离统统为2。因此一定有某时刻队列里有且仅有的是到源点最短路为2的点……依此类推,我们可以证明我们总能先后找到某时刻队列里仅保存了所有到源点最短距离为\(d\)的点。这说明,BFS的访问顺序可以理解为是以“到源点距离”从小到大一圈一圈访问的。如果我们仿照DFS树的样子画出BFS树,会发现BFS树上节点的深度就是到源点的最短距离。

至此,我们已经在线性时间内解决了单源点的无权图最短路问题。

Dijkstra算法

现在考虑带权的图。我们先讨论边权是正整数的情形。

一个直接的想法是,如果我们把一条权为\(k\)的边拆成\(k\)条权为1的边和\(k-1\)个新的节点,那么问题又转化为了无权图的问题,只需要做BFS就可以求出最短路。但这样做使得复杂度多乘了权的上界这个因子。我们应该看看能不能做得更好——当我们把边拆成小的边之后,我们事实上做了很多无用功,因为我们费力求出了到每个“虚拟点”的最短路,这里面可能存在着冗余之处,给我们提供了优化的空间。

在BFS中,顺序是关键。只要确定了访问的顺序,就可以依据上一轮记录的信息来求出最短路。如果我们能够用某种方法“推断”出上述拆边后的BFS中我们是以怎样的顺序访问各个节点的,也就可以求出最短路了。

我们引入一个“闹钟”来表示我们的“推断”。当我们在拆了边之后的图上从源点出发BFS时,我们知道我们将会的做的就是依次访问所有到源点距离为\(d\)的点。因此我们可以在原图上给所有与源点有直接边的点上,把闹钟设置为边权的大小,代表我们“推测”将会在第\(w_i\)秒到达这个节点。当然,这个推测不一定是对的。但在我们到达任何一个“真的”点之前,一切都在我们的掌控之中。所以,对于边权最小的那个边,我们的推测就一定是对的,闹钟的值就是最短路。此时这个点的BFS要开始走向新的边了,因此必须从这个点出发推测与它相邻的点,并相应地给这些点设置闹钟,如果这次设置的值比之前要小了,那就更新为这次的值。同样的,在下一个闹钟响起之前,一切都是在掌控中的,因为我们完全预测了真正的BFS的运行。因此,下一个响起的闹钟也就是相应节点的最短路……

上面这个设闹钟的做法绝对是正确的,因为它不过是用了一种更省时的方法来模拟BFS。我们现在想从中抽象出我们在数学上到底做了什么。我们对于每个点,用它的边权重新设置了周围点的闹钟。而一旦我们开启时间的运行,我们只关心闹钟时刻最小的那个点——因此如果我们抛弃“闹钟”这一概念,就可以这样描述我们的算法过程:对于当前点\(u\),访问所有与它相邻的还没有被确定最短路的点\(v\)。如果\(t_u+w(u,v)<t_v\)就用\(t_u+w(u,v)\)更新\(t_v\)。然后,我们从所有未确定最短路的点中选出\(t\)最小的那个,重复上述操作(如果有多个最小值,理论上我们应该同时取出并更新;但实际上没有必要,因为假如有多个最小值,下一个取出的一定是与上一个的\(t\)一样的点)。也可以理解为,每一次我们从所有不在集合\(S\)的点中选出一个\(t\)最小的,用它更新周围的\(t\),然后把它加入\(S\)。算法结束时,\(S\)内已经包括了所有点,而这些点的\(t\)值就是最短路。

这个算法就是Dijkstra算法,它本质上是BFS的一个推广。从推广的过程中,我们使用时间来模拟边权,这就意味着我们无法保证这一算法对负权边的适用性(事实上,有反例可以证明Dijkstra对负权图是错误的),因为时间是不能倒着走的。但我们能做的推广是把正整数推广到正实数(虚拟点的多少而已)。Dijkstra对正权图是始终正确的。

Dijkstra需要耗费比BFS更多的时间,因为它的取点顺序不是简单地取出队首,而是需要取出“队列”中\(t\)最小的那个点。这样的队列称为“优先队列”。如果我们用枚举来找最小的点,Dijkstra的复杂度是\(O(|V|^2+|E|)\)的。但好在我们已经有数据结构能帮助我们维护“支持插入和删除的集合中的最值”,最常见的就是一种叫“堆”的二叉树,它可以在\(O(\log n)\)的时间内完成插入和删除,\(O(1)\)访问最值。这样,Dijkstra的复杂度就被优化为了\(O((|V|+|E|)\log |V|)\)(每访问一条边都可能会进行依次更新,更新就等价于堆上的插入,因此\(|E|\)被移进了括号内)。

标签:队列,短路,源点,BFS,闹钟,我们
From: https://www.cnblogs.com/qixingzhi/p/17277152.html

相关文章

  • CF(2E) Keshi in Search of AmShZ (图论,最短路,建边权值变形)
      思路: 关键是操作2的性质:随机找->找一个路径最长的点操作1,阻止建边顾名思义, 发现和最短路很想,从n到每一个点的权值嘛改变权值更新方式,边的权值为:va......
  • P4366 [Code+#4]最短路
    P4366[Code+#4]最短路一个图有两层:一层完全图,每对\(u\),\(v\)间都有一条边权为\(u\oplusv\)的边。一层给定图,边信息完全给定。这层图的边数\(m\le5\times10......
  • 最短路
    一.Bellman-Ford主要适用场景:1.这主要是一个用来判断是否存在负环的2.当边权可为负数时Dijkstra算法不成立!这个可以很容易去判断!!!所以此时只能去弄Bellman-Ford!!Bellm......
  • 使用SQL语句实现最短路线问题
    今天学习了一种直接用sql语句实现查询最短路径的方法,为我们的系统开发提供了便利。Stringsql="WITHRECURSIVEtransfer(start_station,stop_station,stops,path)......
  • 逻辑运算符短路特性
    &&短路特性遇到假即为假,不会判断下一组表达式||短路特性遇到真即为真,不会判断下一组表达式......
  • 「最短路径树」黑暗城堡
    本题为3月17日23上半学期集训每日一题中B题的题解题面题目描述在顺利攻破Lordlsp的防线之后,lqr一行人来到了Lordlsp的城堡下方。Lordlsp黑化之后虽然拥有了......
  • 多源最短路Floyd本质理解
    \(Floyd\)总结复习Floyd是动态规划的典型体现,其思想从集合角度用闫氏DP分析法即可其关键的性质理解:即外层循环k的理解。\(dist[k][i][j]\)代表(k的取值范围是从1到n),在考......
  • 简单图最短路径
    graph={'保安':['巡检','巡逻','监控'],'监控':['监视','密切','白领'],'巡逻':['白领','蓝领','科学家'],'白领':['销售','大堂经理',&#......
  • 多源最短路算法——Floyd算法(转载)
    1.多源最短路简介:我们知道单源最短路是指从某一个源点到图中的其它顶点的最短路。多源最短路就是指每一个点到图中其他顶点的最短路。那么有的人肯定想我知道求单源最短路......
  • 最短路
    #include<bits/stdc++.h>usingnamespacestd;intg[205][205];intmain(){memset(g,0x3f,sizeofg);intn,m,k;cin>>n>>m>>k;while(m--){in......