首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

图论算法 有图有代码 万字总结 向前辈致敬(4)

图论算法 有图有代码 万字总结 向前辈致敬(4)

Dijkstra算法前面的广度优先搜索中的图是无权图,而如果一旦变成了加权图,那么问题就变得困难起来了。
对于每个顶点,我们标记为known以及unknown,和上面一样,还得有一个距离的dv。与无权最短路径一样,Dijkstra算法也是按阶段进行,在每个阶段选择一个顶点v,它在所有unknown顶点中具有最小的dv,同时算法声明从s到v的最短路径是known的。然后紧接着,不断的进行下去即可。
那么这个算法到底是怎么回事了?请看下图。

图中已经对权重做好了标记,以v1作为切入点,因此初始情况如下左图。
v1此时已经是known的了,而其有2个邻接点v2和v4,因此可以调整为如下右图。正无穷图标标识没有连通。pv表示前一个邻接点。

毫无疑问这里会接下来走到v4去,因为v4的权重为1比v2的权重为2要小。调整为如下左图。

可能你已经看到了上图中的右图而好奇为什么下一步是v2,但是v4根本不能走到v2。因为v4能够走到的,比如v3,权重从v1开始一共是3,这比从v1到v2还要大。于是就跳转回到了v2。
下一步便走到了v5,因为只有值为3的权重,同样的v3也是,于是它们俩被双双标记为known。如下左图所示。
紧接着走到了v7,同时v6下调到了5+1=6得到了如下右图。至于为什么要做这个调整,是因为此时v1到v7的加权为1+4=5,而v7到v6的加权为1,所以就有了这个调整。

最后便顺势走到了v6完成了整个Dijkstra算法,它们都已被标记为known。

在后面还将会有一种斐波那契堆,针对Dijkstra算法做了优化,欢迎大家的继续关注。
具有负边值得图而如果一个图具有负的边值,那么Dijkstra算法就行不通了。这是因为一个顶点u被声明为known后,那就可能从某个另外的unknown顶点v有一条回到u的负的路径。而“回到”就意味着循环,前面的例子中我们已经知道了循环是多么的……
问题并非没有解决的办法,如果我们有一个常数X,将其加到每一条边的值上,这样除去负的边,再计算新图的最短路径,最后把结果应用到原图上。然后这个解决方案也是布满了荆棘,因为居多许多条边的路径变得比那些具有很少边的路径权重更重了。如果我们将s放到队列中,然后再每一个阶段让一个顶点v出队,找出所有与v邻接的顶点w,使得dw>dv+cv,w,然后更新到dw和pw,并在w不在队列中时将它放到队列中,可以为每一个顶点设置一个位(bit)以指示它在队列中出现的情况。
无环图如果图是无环的,则可以通过改变声明顶点为known的顺序,或者叫做顶点选取法则来改进Dijkstra算法。这种方法通过拓扑排序来选择顶点,由于选择和更新可以在拓扑排序执行的过程中执行,因此新的算法只需要一趟就可以完成。
通过下面这个动作结点图(activity-node graph)来解释什么是关键路径分析(critical path analysis)再合适不过了。一条边(v,w)表示动作v必须在动作w开始前完成,如前面说描述的那样,这就意味着图必须是无环的。

为了进行这些运算,我们把动作结点图转化成事件结点图(event-node graph),每个事件对应于一个动作和所有与它相关的动作完成。

所以现在我们需要找出事件的最早完成时间,只要找出从第一个事件到最后一关事件的最长路径的长。因为有正值回路(positive-cost cycle)的存在最长路径问题常常是没有意义的。而由于事件结点图是无环图,那就不需要担心回路的问题了,这样一来就不用有所顾忌了。
以下是最早完成时间。

以下是最晚完成时间。

借助顶点的拓扑排序计算最早完成时间,而最晚完成时间则通过倒转拓扑排序来计算。
而事件结点图中每条边的松弛时间(slack time)代表对应动作可以被延迟而不推迟整体完成的时间量,最早完成时间、最晚完成时间和松弛时间如下所示。

某些动作的松弛时间为0,这些动作是关键性的动作,它们必须按计划结束。至少存在一条完成零-松弛边组成的路径,这样的路径是关键路径(critical path)。
继承事业,薪火相传
返回列表