使用修改后的弗洛伊德战争打印给定节点之间的最短路径

Posted

技术标签:

【中文标题】使用修改后的弗洛伊德战争打印给定节点之间的最短路径【英文标题】:Printing shortest path b/w given nodes using modified floyd warshall 【发布时间】:2013-11-17 18:55:12 【问题描述】:

我阅读了Wikipedia 给出的方法,通过修改 Floyd Warshall 算法在图中以两个给定点打印短路路径。我对此进行了编码,但它并没有真正给出预期的输出:

    minimumDistanceMatrix[i][j]中的所有元素初始化为图中各自的权重,将矩阵shortestPathCalculatorMatrix [i][j]中的所有元素初始化为-1。

    然后:

    // Find shortest path using Floyd–Warshall algorithm
    
    for ( unsigned int k = 0 ; k < getTotalNumberOfCities() ; ++ k)
       for ( unsigned int i = 0 ; i < getTotalNumberOfCities() ; ++ i)
          for ( unsigned int j = 0 ; j < getTotalNumberOfCities() ; ++ j)
              if ( minimumDistanceMatrix[i][k] + minimumDistanceMatrix[k][j] < minimumDistanceMatrix[i][j] )
              
                    minimumDistanceMatrix[i][j] = minimumDistanceMatrix[i][k] + minimumDistanceMatrix[k][j];
                    shortestPathCalculatorMatrix [i][j] =  k;
              
    

    然后:

    void CitiesMap::findShortestPathListBetween(int source , int destination) 
    
         if( source == destination || source < 0 || destination < 0)
            return;
    
         if( INFINITY == getShortestPathBetween(source,destination) )
            return ;
    
         int intermediate = shortestPathCalculatorMatrix[source][destination];
    
         if( -1 == intermediate )
         
            pathCityList.push_back( destination );
            return ;
         
    
         else
         
            findShortestPathListBetween( source, intermediate ) ;
            pathCityList.push_back(intermediate);
            findShortestPathListBetween( intermediate, destination ) ;
    
            return ;
         
    
    

P.S:pathCityList 是一个向量,在调用 findShortestPathListBetween 之前假定为空。在此调用结束时,此向量中包含所有中间节点。

有人可以指出我可能出错的地方吗?

【问题讨论】:

您应该提供代码输入和输出的简短示例。或者至少描述一下你得到的输出是什么样子的。 实际上根本不需要修改——弗洛伊德-沃歇尔的算法直接为您提供了最短路径(***的文章具有高度误导性;罗伯特·弗洛伊德的公式给出了长度,沃歇尔给出了路径;加在一起,它们给出了两个都)。但是如果你只对一条最短路径感兴趣,那么这个算法无论如何都不合适,还有更有效的算法(Dijkstra 算法)。 @KonradRudolph 我有兴趣打印实际路径,而不是 Floyd Warshal 通常提供的路径长度,因此需要进行修改。 @Amit 查看我修改后的评论。***的文章是错误的,FW,以其规范形式,确实提供两者。 【参考方案1】:

不迭代索引而是迭代顶点要容易得多(也更直接)。此外,每个前驱(通常表示为π,而不是next)都需要指向它的前驱,而不是当前的临时顶点。

给定一个 |V|×|V|距离的邻接矩阵dist,初始化为无穷大,|V|×|V|邻接矩阵next 带有指向顶点的指针,

for each vertex v
    dist[v, v] ← 0
for each edge (u,v)
    dist[u, v] ← w(u,v)  // the weight of the edge (u,v)
    next[u, v] ← u

for each vertex k
    for each vertex i
        for each vertex j
            if dist[i, k] + dist[k, j] < dist[i, j] then
                dist[i, j] ← dist[i, k] + dist[k, j]
                next[i, j] ← next[k, j]

请注意,我已将三个嵌套循环更改为迭代顶点而不是索引,并且我已将最后一行固定为引用前一个节点而不是任何中间节点。

可以在例如scipy.sparse.csgraph 中找到看起来几乎像伪代码的上述实现。

路径重建从末尾开始(下面代码中的j)并跳转到j的前身(next[i, j]),直到到达i

function path(i, j)
    if i = j then
        write(i)
    else if next[i, j] = NIL then
        write("no path exists")
    else
        path(i, next[i, j])
        write(j)

【讨论】:

我应该用什么初始化矩阵next[i][j]?而这个矩阵唯一被修改的地方是语句next[i][j] ← next[k][j]。它不会继续将默认值从一个位置覆盖到同一矩阵的另一个位置吗? @Amit 很好的观察,忘记提了(尽管链接页面提到了,虽然不是在伪代码中,只是 Java 实现)。我会补充一点,给我一秒钟……【参考方案2】:

有点晚了,但上面的代码有缺陷....它不应该是next[i][j]=next[k][j],但找到它的正确代码是next[i][j]=next[i][k]

在示例输入上自己尝试一下,您就会知道为什么这样有效,以及为什么前一个错误

【讨论】:

添加更多描述【参考方案3】:

这是下面的实现。 Why don't try a problem on it!享受CP!!

   // g[][] is the graph
   // next[i][j] stores vertex next to i in the shortest path between (i,j)
      for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
          if(g[i][j]==0)g[i][j]=inf;  // there is no edge b/w (i,j)
          else next[i][j]=j;    // if there is an edge b/w i and j then j should be next to i
        
      

      for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++)
            if(g[i][j]>g[i][k]+g[k][j])
              g[i][j]=g[i][k]+g[k][j];
              next[i][j]=next[i][k];  // we found a vertx k next to i which further decrease the shortest path b/w (i,j) so updated it
            
          
        
      
      // for printing path
      for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
          int u=i,v=j;
          print(u+" ");
          while(u!=v)
            u=next[u][v];
            print(u+" ");
          
          print("\n");
        
      

【讨论】:

以上是关于使用修改后的弗洛伊德战争打印给定节点之间的最短路径的主要内容,如果未能解决你的问题,请参考以下文章

求最短路径长度--简单易懂

算法导论——最短路径问题(Floyd算法)

Floyd 的最短路径算法 C++

Floyd算法

最短路径算法——清晰简单的弗洛伊德算法(Floyd)

最短路径算法之Floyd算法