【问题标题】:Finding fastest path at additional condition在附加条件下寻找最快路径
【发布时间】:2012-12-20 11:43:02
【问题描述】:

我想知道,当无向图中存在多个直接连接时,Dijkstra 算法是否能正常工作。

例如:

我想使用 Dijkstra 找到最快的路径,但是还有一个附加条件。边缘上所有附加数据的总和不能> = x。因此,如果出现 weight: 3 的边使用错误,我的程序将尝试使用第二条边。

编辑: 我的任务是在边缘的additional_data之和不能高于x的附加条件下找到最快的路径。你能告诉我如何处理这个问题吗?

edit2:(设置赏金)

我一直在研究互联网,直到找到link。 有一个关于如何做我要求的事情的解释。 (中上级

我现在正在尝试以某种方式使用它 2 天,但我担心我无法正确理解此算法。我想请你们中的一些人帮助我解决这个问题,通过更多地解释我的例子(几个第一步)。示例如下:

【问题讨论】:

  • 平行边不会破坏 Dijkstra,但附加条件会。
  • 这是一个完全相同的副本,但原始问题没有解决方案 - 仅声明其他地方存在解决方案(并且 SPOJ 的 815 人已经找到了解决方案)。
  • 不是 完全重复——另一个问题根本没有提到一对节点之间可能存在两条或更多路径的可能性,也没有附加在下摆上的额外条件。
  • 这个问题是 np-complete,但如果 sum(additional_data) 的界限不是相当低,您将需要研究近似算法

标签: c# algorithm dijkstra


【解决方案1】:

我认为您可以修改 Dijkstra 的算法来处理这个问题。 Dijkstra 的算法基本上是通过逐步构建一个表来列出到每个节点的最短路径来工作的。您将改为构建一个表格,列出以给定成本到每个节点的最短路径。或者更确切地说,以给定的成本或更少,即在给定的预算内。

更正式地说,您可以将原始图转换为另一个图,然后将 Dijkstra 应用于该图。假设additional_data成本始终为整数,则变换为:

  1. 获取每个原始节点 n,并为从 0 到预算值(您可以容忍的最大附加数据总和)的 c 的每个整数值创建一组节点 (n, c)
  2. 获取每个原始连接 n1 -> n2 权重为 w 和附加数据 a,并创建一组从每个节点 (n1, c) 到节点 (n2, c+a) 的连接,每个连接权重为 w

原始图模型中的节点在空间中的位置。转换后的图模型中的节点在状态空间中的位置,其中状态变量是位置,以及到目前为止花费的预算金额。

如果您想以 x 的预算从 A 到 Z,那么您只需使用 Dijkstra 算法找到从 (A, 0) 到其中一个节点 (Z, c

编辑:我已经实现了修改后的 Dijkstra 算法:https://bitbucket.org/twic/roadsproblem。症结在src/solver.py

【讨论】:

  • 说实话,我不太明白你的意思。假设我的 max_sum = 200。我需要将每个节点转换为转换图中的 200 个节点的集合吗?
  • 是的。嗯,不。从“更正式”的角度来看,我正在描述一些可行的简单方法,这使得准证明您想要做的事情是可能的变得容易。我实际上不会那样实现它。相反,我会按照我在第一段中所说的去做,我意识到我根本没有解释清楚,但这相当于懒惰地构建转换后的图。
  • 我很高兴能更详细地解释这一点,也许有一些代码,但我必须出去吃晚饭。也许以后!
  • 希望收到您的来信:P
  • 我认为在java3z.com/cwbwebhome/article/article17/acm862.html(本页第二个程序)有Java 代码来实现Tom 算法的惰性版本。
【解决方案2】:

这里解释了您找到的算法将如何处理您的示例问题。

问题是找到node onenode four 之间的最短路径,附加条件是沿途累计成本不应超过7

我们想要找到的解决方案是首先从node one 到节点node two,距离为190,成本为4。然后使用距离195 和成本3 的路径从node twonode four。路径的总距离为385,成本为7

第 1 步

那么算法是如何找到这个的呢?第一步是设置矩阵minArray(i,j),就像你做的那样。数组的元素(i,j) 包含您必须经过的距离才能到达节点j,而剩余的资金正好是i

一开始没有访问过的元素,因为我们从node one7 "monies" 开始,左上角的元素设置为零。上表中的空格对应数组中设置为infinity的值。

第 2 步

接下来,我们找到数组的最小值,这是位置(remaining money, node) = (7,1) 处的零。该元素设置为visited(使用与minArray 大小相同的矩阵visitedArray 跟踪元素的状态),这意味着我们选择了node one。现在所有连接到node one 的节点都通过遍历相应的边来更新值。

这里是minArray(6,3) = 191,这意味着我们已经走了191 的距离才能到达node three,剩下的钱是6,因为我们已经支付了1 的费用才能到达那里。以同样的方式将minArray(3,2) 设置为190。红色方块标记元素 (7,1) 被访问。

第 3 步

现在我们再次找到最低的未访问元素(minArray(3,2) = 190),将其设置为visited,并更新所有连接到它的元素。这意味着距离累加,剩余的钱是通过从当前值中减去成本来计算的。

注意从node two回到node one太贵了。

第 4 步

下一步,选择minArray(6,3) = 191 如下所示。

第 5 步

三步后数组看起来像这样。这里选择了等于382 和等于383 的两个元素。请注意,元素的值只有在改进(即低于)当前值时才会更新。

第 6 步

数组继续填满,直到所有元素都被访问或仍然具有无限值。

最后一步

最后一步是通过找到第四列的最小值来找到总距离。我们可以看到最小值 minArray(0,4) = 385 对应于正确的解。

注意:如果第四列的所有值都是无限的,则意味着没有有效的解决方案。该算法还规定,如果第四列中有多个相等且最小距离的值,则选择最便宜的一个。

【讨论】:

    【解决方案3】:

    您的附加条件使问题变得更加困难。看着它,我认为你唯一能做的就是找出源和目标之间所有可能的路径,按总边权重排序,然后一一检查你的附加条件是否成立。

    然而,寻找两个顶点之间所有可能路径的问题是NP-Hard。稍加修改的 DFS 版本或许可以解决问题,但可能并非在所有情况下都如此。

    【讨论】:

      【解决方案4】:

      我不认为 Dijkstra 的算法是解决这个问题的好方法,因为所需的距离不仅是源节点和目的地。这是一个基于A*搜索算法的解决方案。\

      首先,基于weight 执行FolydWarshall,然后基于additional_data 得到图中每个节点对的最少weight 和最少additional_data

        FloydWarshall(Weights);
        FloydWarshall(Additional_datas);
      

      其次,我们基于优先级队列执行 A* 搜索,其元素如下结构(以 c 代码为例。)优先级队列将自动获取所有候选中的 weights_sum 最少。 weights_expected 是通过当前节点到目标节点的路径的最佳猜测,而 weights_now 是当前权重

        struct NODE
          {
              int node;
              int weights_expected;
                  int weights_now;
              int additional_datas_now;
                  bool visited;
          };
          bool operator < (const NODE &A,const NODE &B)
          {
              return A.weights_expected>B.weights_expected || (A.weights_expected==B.weights_expected && 
         A.additional_datas_now>B.additional_datas_now);
          }
      

      在A*搜索算法中,

      1) we first put the source node into priority queue. 
        2) while Priority Queue is not empty:
              Set **A** equal to the head of priority queue and pop out the head of priority queue. 
              A.visited=True;
              if A is the destination node **Dest**, **return** A.weights_expected. 
              For each neighbors **B** of node **A**, 
                if A.visited==False **and** A.additional_datas_sum+|AB|.additional_data+Additional_datas[B][Dest]<=x, 
                     1) B.additional_datas_now=A.additional_datas_now+|AB|.additional_data;    
                     2) B.weights_now=A.weights_now+|AB|.weight;
                     3) B.weights_expected=B.weights_now+Weights[B][Dest];
                     3) push node B into priority Queue. 
         3) Print "Do not find a proper path" //if code came to here, that means the if in 2) do not return a value. 
      

      A* 搜索仍然是 NP 难的,因为在最坏的情况下它必须搜索每条可能的路径。然而,它比简单的 DFS 搜索要快得多,并执行大量搜索路径切割。

      【讨论】:

        【解决方案5】:

        您可以制作节点之间成本为 0 的副本,以添加第二条可能的路径。

        像这样(伪代码)

        Node 1____
        |         |
        |path1    |
        |cost=3   |path2 cost=5
        |         |
        Node 2____/
        

        变成这样:

        Node 1____cost=0____Node 1a
        |         path 1a     |
        |path1                |path2
        |cost=3               |cost=5
        |                     |
        Node 2________________/
        

        不确定这是否可行,但这是一个想法。

        【讨论】:

          【解决方案6】:

          附加条件将破坏 Dijkstra。可以这样想:如果你在图中有一条路径 A->B 和一条边 B->C 在图中,那么涉及 B 的最短路径 A->C 肯定是 A->B 的最小路径->C。在您的情况下,此条件不成立,因为虽然 A->B 和 B->C 可能有效,但 A->B->C 可能无效。

          好的,这就是你抓起一张纸试一试的地方。

          如果您查看图表,并假设您想从 (1) 转到 (4),请注意如何通过引入以下边来消除 (3):

          • (1)->(4), [390, 2]
          • (1)->(2), [383, 3]
          • (2)->(4), [391, 3]

          一旦消除了除直线以外的所有边,问题就变得容易一些:对于每个节点,您可以跟踪达到目标需要多少 [距离,额外]。您不必存储额外的 > max 或 'remaining additional'

          现在最好的解决方案是在最后一个节点(或第一个,取决于您如何订购它)中距离最小的那个。如果在路上,你保留了你是如何到达那里的指针(例如,如果你更新矩阵中的值也存储使它改变的元素),你也可以回溯路径。

          在这里您应该注意,当问题处于非消除形式时,您可以使用您建议的矩阵执行相同操作:x 轴作为节点,y 轴作为“每个节点的列表”。

          应该可以的。

          【讨论】:

            【解决方案7】:

            您可以使用 bellman-ford 算法,假设您的 additional-data 是 bellman-ford 算法中的边数参数

            【讨论】:

              【解决方案8】:

              这个问题是NP完全的。没有一种算法比多人解释的算法更有效(Tom Anderson,user1884905)。

              证明: 通过减少非负数的子集和。

              取一个子集和(N 个数字)的实例 A。构建一个图,其中有 N+1 个节点。对于节点 i 和 i+1 ,制作 2 条路径,一条权重=0,additional_data=A[i],另一条权重=A[i],additional_data=0。选择 x(additional_data 总和的限制)。

              请注意,该算法必须最小化权重之和,因此它也会最大化附加数据之和。所以第一种选择的路径将是与子集和问题结果中的数字相关的路径。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2021-07-05
                • 2012-10-24
                • 1970-01-01
                • 2020-09-28
                • 2013-10-07
                • 2018-12-31
                • 2017-04-09
                相关资源
                最近更新 更多