我有以下图表:
我需要计算从 p1 到 pn 节点访问 pk 节点的最短路径( pk 不是开始或结束节点)。主要问题在于限制解决方案路径不应多次访问任何节点。因此,可能根本没有解决方案:例如,当节点 pk 只有一个非无限连接( pj 节点)时,路径必须去通过 pj 节点两次。
是否有任何可接受的算法来解决问题?
答案 0 :(得分:0)
我将建议修改受其启发的Winata算法 - 除了简单的情况之外它并不是很好,但我开始怀疑这是一个难题。
Winata建议我们计算从p1和pn到pk的最短路径,并合并这些路径以获得从p1到pn的路径。如果这返回满足额外约束的答案,即不会多次访问任何节点,则可以解决问题。问题在于,此处找到的解决方案可能无法满足该约束条件。
我建议我们希望Winata的答案与正确的答案没有太大区别,并开始列举从p1到pk以及从pk到pn的所有最短路径。给定这些枚举,我们可以按顺序枚举从p1到pk和pk到pn的所有路径组合,并查找满足额外约束的第一个路径,它必须是最短的路径。
http://www.vldb.org/pvldb/vol4/p1028-golenberg.pdf上的论文描述了一种列举问题解决方案的一般方法(Lawler-Murty),给出了一种找到问题最佳解决方案的方法。例如,在附录A第9页中,它解决了从源到目的地按照长度顺序生成路径的问题。
这种方法的一个缺点是枚举方法需要越来越多的存储来跟踪问题的减少版本的堆栈。另一种方法是使用Winata的方法 - 它解决了一个忽略问题约束之一的简单问题 - 提供分支定界方法的下限。 Winata方法提供的解决方案可能无效,但其成本是任何有效解决方案成本的下限,这正是分支和绑定所需的。
我怀疑分支和绑定实际上是一个更好的解决方案,所以我会尝试更全面地解释它。在http://en.wikipedia.org/wiki/Branch_and_bound处有分支和绑定的一般描述。它是一种非常类似于深度优先搜索的递归搜索,其中每个节点对应于问题的部分解决方案。在这里,部分解决方案将是路径的建议中心部分,其中一些链路通向pk,以及一些远离它的链路。当您对部分解决方案树进行深度优先搜索时,您将跟踪到目前为止找到的最佳解决方案。当您递归到一个节点时,您尝试并显示该节点下面没有完整的解决方案可能比目前为止找到的最佳解决方案更好,因为如果是这样,您无需再调查通过扩展该部分解决方案而形成的任何解决方案,并且您可以返回深度优先搜索,而不是在该节点下面递归。
当你有一个部分解决方案,如(Pk-1,Pk,Pk + 1)时,你可以计算出从P1到Pk-1以及从Pk + 1到Pn的最短路径。这可能不合法,但它仍然是一个下限 - 没有法律解决方案(Pk-1,Pk,Pk + 1),因为它的中心部分可能比你制定的路径总数做得更好。因此,如果此总和并不比目前为止找到的最佳解决方案更好,则无需通过进一步扩展该部分解决方案来调查任何其他部分解决方案。如果三个部分的组合是合法的解决方案,访问每个节点不超过一次,那么您已经找到了可能的答案,并且您再次无需在树中调查此下的任何节点。如果结果不是合法路径,那么您将不得不尝试通过在每个可能的合法方式中在其前面(或末尾)添加链接来扩展部分解决方案,递归地在树中找到下面的部分解决方案。
如果根本没有解决方案,您应该会发现深度优先搜索总是终止,无法在某个深度扩展每个部分解决方案,而无需找到完整的合法路径。如果有解决方案并且您很幸运,您将很快找到一条成本非常低的合法路径,以便您可以证明没有任何其他部分解决方案可以扩展到击败它。 - 但不能保证这个过程可能需要多长时间 - 对于可能需要很长时间才能实现的难题。
答案 1 :(得分:0)
另一种解决方案:
由于这是一个无向图,解决方案并不困难,即:
执行以下操作时,通过另一个名为pathTo
的数组来跟踪每个节点的父节点,该数组将包含PT(k, p)
,其中k
是当前节点,沿最短路径连接到它的父级是p
首先运行dijkstra算法,以获得从p1
到连接到pk
的所有点的最短路径。因此,如果pk
有2条边,则您希望运行此算法,直到放宽这些边。
接下来,您使用广度优先搜索来查找从连接到pk
到pn
的任何顶点的最短路径。这意味着您将获取pk
具有边缘的所有顶点并将其用作bfs的起点。请注意,由于这是bfs,因此您只需运行一次,并且应该在达到pn
后终止。
如果bfs在浏览整个图表之前无法终止,则没有路径经过pk
并以pn
结束。否则,您将使用pn
数组
p1
追溯到pathTo
请注意,可以轻松调整此方法,以便找到从p1
到pn
并通过多个点的最短路径