【问题标题】:Shortest path that traverses a list of required edges遍历所需边列表的最短路径
【发布时间】:2015-01-24 20:54:01
【问题描述】:

我有一个有向图,如下所示:

我想找到从开始到结束最便宜的路径,其中橙色虚线都是有效路径所必需的。

自然的最短路径是:开始 -> A -> B -> 结束,结果成本 = 5,但我们还没有满足所有必需的边缘访问。

我想要找到的路径(通过一般解决方案)是 Start -> A -> B -> C -> D -> B -> End 其中成本 = 7,我们有满足所有必需的边缘访问。

有人对如何要求这样的边缘遍历有任何想法吗?

【问题讨论】:

  • 所以这是最短的步行,而不是路径,对吧?
  • 是否保证一次walk可以覆盖所有边缘? (例如,如果没有边缘数据库怎么办?)
  • @JuanLopes,假设在本例中您将遍历 1 个节点 2x 次,则它符合步行的条件,而不仅仅是一条路径。我认为所有示例实际上都会遍历一些 > 1x 的节点。
  • @j_random_hacker 不保证单次步行可以覆盖所有边缘。我相信这是问题中最困难的部分。
  • 在最一般的形式中,这个问题是 NP-hard。我们可以对图表做任何假设吗?证明:如果您从旅行推销员的实例开始,您可以将每个节点拆分为两个节点对:一个具有传入边,一个具有传出边。在每个节点对之间创建一条边,并要求该边成为解决方案的一部分。然后你就有了这个问题的一个例子,所以如果你能在多项式时间内解决这个问题,你可能会赢得巡回奖。

标签: algorithm graph shortest-path


【解决方案1】:

R 为所需边的集合,F = |R|。令 G 为输入图,t (resp. s) 为所请求路径的起点(或终点)。


预处理:一堆 Dijkstra 的算法运行...

第一步是创建另一个图表。该图将恰好有 F+2 个顶点:

  • R 中的每个边对应一个
  • 一个用于您要计算的路径的起点t
  • 一个用于您要计算的路径的终点s

要创建此图表,您必须执行以下操作:

  1. G 中删除 R 中的每条边。
  2. 对于 R 中的每条边 E = (b,e):
    1. 计算从tb的最短路径和从es的最短路径。如果存在,则在“新图”中添加一条连接 sE 的边,衡量相关最短路径的长度。
    2. 对于 R \ {E} 中的每条边 E' = (b', e') :
      1. 计算从 eb' 的最短路径。如果存在,则在新图中添加一条从 EE' 的边,衡量该最短路径的长度。将计算出的路径作为有效负载附加到相关边。
      2. 将计算出的路径作为有效负载附加到该边缘

构建此图的复杂度为 O((F+2)².(E+V).log(V)) 其中 E ( resp. V) 是原始图中边(resp. vertices)的数量。


详尽搜索最佳路径

从这一点开始,我们必须在新创建的图中找到最短的哈密顿路径。不幸的是,这项任务是一个难题。我们没有比探索每条可能的路径更好的方法了。但这并不意味着我们不能巧妙地做到这一点。

我们将使用回溯执行搜索。我们可以通过维护两个集合来实现这一点:

  • 当前探索的顶点列表:KK代表已知
  • 当前未知顶点列表:UU 代表 Uknown

在深入挖掘算法定义之前,这里是主要思想。除了探索新图中可能路径的整个空间之外,我们无能为力。在每一步,我们都必须做出决定:下一步我们采取哪条边?这会导致一系列决策,直到我们无法再移动或到达s。但是现在我们需要返回并取消决定,看看我们是否可以通过改变方向做得更好。要取消决定,我们会这样处理:

  • 每次我们被卡住(或找到路径)时,我们都会取消上一次做出的决定
  • 每次我们在某个时刻做出决定时,我们都会跟踪哪个决定,因此当我们回到这一点时,我们知道不要做出同样的决定并探索其他可用的决定。
  • 我们可能会被卡住,因为:
    • 我们找到了一条路径。
    • 我们无法继续前进(没有我们可以探索的边缘,或者我们唯一可以采用的边缘会过多地增加当前部分路径 - 它的长度变得比当前找到的最佳路径的长度要长)。

最终的算法可以这样总结:(我给出了一个迭代实现,可以发现递归实现更容易和更清晰)

K[], L[0..R+1][]U ← V(其中 V 是工作图中每个顶点减去开始和结束顶点的集合 t em> 和 s)。最后让li←0和best_path_length←∞和best_path[]

而(i ≥ 0):

  1. U[]
    1. cU.popFront()(我们拿U的头)
    2. L[i].pushBack(c)
    3. 如果i == R+1 AND (l == 重量(cur_path.back(), s) + l) best_path_length:
      1. best_path_lengthl
      2. 最佳路径 ← cur_path
    4. 如果K.tail()c之间有边e,并且weight(e)+lbest_path_length:(如果K为空,则将K.tail()替换为t 在前面的语句中)
      1. K.pushBack(c)
      2. ii+1
      3. lweight(e) + l
      4. cur_path.pushBack(c)
  2. U 的末尾连接L[i]
  3. L[i][]
  4. ii-1
  5. cur_path.popBack()

在 while 循环 (while (i ≥ 0)) 结束时,best_path 将保存最佳路径(在新图中)。从那里你只需要获取边的有效载荷来重建原始图中的路径。

【讨论】:

  • "对于 R 中的每个边 E = (b,e):" -- 但 R 中最初没有边,所以这将执行 0 次。您的意思是“在 G 中”吗?
  • @j_random_hacker 我将 R 定义为所需边的集合。所以 R 不会为空!
  • 抱歉,我让 R 对新图表感到困惑!
  • 感谢您的彻底回复。我会用它运行,并让你知道当我可以编写 ~this 时我的表现如何。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-05
  • 2017-12-06
  • 2019-09-27
  • 1970-01-01
相关资源
最近更新 更多