在O(E logV)中查找图中的单调最短路径

时间:2014-04-05 03:12:37

标签: algorithm language-agnostic graph-theory graph-algorithm shortest-path

来自this page的广告素材问题34。

  

单调最短路径。给定边加权有向图,找到从s到每个其他顶点的单调最短路径。如果路径上每条边的权重严格增加或严格减小,则路径是单调的。

     

部分解决方案:按升序放松边缘并找到最佳路径;然后按降序放松边缘并找到最佳路径。

我的问题:

假设我们按降序放松边缘,并且我们可以在一个点上选择多于1个边缘。我们将在什么基础上选择下一个优势?理想情况下,我们应该选择较小的边缘,因为它会最小化到该顶点的距离。但是,如果离开它的所有边都具有大于当前边缘权重的权重,那么这样做可能导致该顶点没有其他路径。

那么,我们如何解决这个问题?

2 个答案:

答案 0 :(得分:4)

这个问题可以通过改进的Dijkstra算法来解决。重点是放松应该不是在每个图形节点(通常)中使用min操作,而是在优先级队列中进行。

以下是通常Dijkstra算法的修改列表。我认为只有边缘的放松按升序排列,这导致严格减少最短路径(增加最短路径,改变第2和第4项):

  1. 通过对每个节点的出局边缘进行排序(按重量)预处理图形。
  2. 每个节点应包含传出边列表中的位置(由最轻边的位置初始化)。
  3. 不需要优先级队列来支持“减少”操作(因此可以通过简单的min-heap实现)。每个顶点都插入优先级队列,然后永远不会改变,直到它出现在队列的顶部(因此每个顶点可以在队列中多次表示)。队列条目由一个键(通常是路径长度),顶点和传入边缘的权重组成。因此,我们可以假设优先级队列包含传入的边而不是顶点。
  4. 放松程序:从队列中弹出边缘(以及因此边缘结束的顶点);对于顶点的所有外出边缘,以递增的顺序,从图形节点中存储的位置开始,当输出边缘的权重大于或等于输入边缘的权重时结束,将输出边缘推送到优先级队列并提前存储位置。
  5. 该算法保证每条边最多处理一次(如果我们考虑严格减少和严格增加路径,则保留两次),因此其复杂度为O(E log E)。

    C ++ 11实现:

    void getDecreasingSP(Vertices& vertices, Edges& edges, int src)
    {
        for (auto& v: vertices)
            sort(begin(v.outEdges), end(v.outEdges),
                 [&](int from, int to)
                 {
                     return edges[from].weight < edges[to].weight;
                 });
    
        PQ pq;
        auto& src_v = vertices[src];
        for (auto e: src_v.outEdges)
        {
            QEntry entry {edges[e].weight, e};
            pq.push(entry);
            ++src_v.pos;
        }
    
        while(!pq.empty())
        {
            QEntry top = pq.top();
            pq.pop();
            auto& v = vertices[edges[top.inEdge].to];
    
            while (v.pos < int(v.outEdges.size()) &&
                edges[v.outEdges[v.pos]].weight < edges[top.inEdge].weight)
            {
                auto e = v.outEdges[v.pos];
                edges[e].backPtr = top.inEdge;
                QEntry entry {top.pathWeight + edges[e].weight, e};
                pq.push(entry);
                ++v.pos;
            }
    
            if (v.backPtr == -1)
                v.backPtr = top.inEdge;
        }
    }
    

    另见working code on Ideone。图形的可视化(由此代码在Graphviz的帮助下生成),其中突出显示严格减少的最短路径之一:

    enter image description here

答案 1 :(得分:2)

我使用改良的Dijkstra算法来解决它: 例如,如果我们想按升序找到源和其他每个顶点之间的最佳路径,请使用优先级队列PQ:

  1. PQ.add(源,0)
  2. 对于其他顶点,PQ.add(vertex,infinity)
  3. 当PQ不为空时,令顶点i = PQ.removeSmallest(); 放松i的所有上升沿;

以权重从i到p放松边缘:

if (disTo[p] > disTo[i] + weight && weight > weight[i, edgeTo[i]) {
   disTo[p] =  disTo[i] + weight;
   edgeTo[p] = i;
   PQ.changePriority(p, disTo[p]);
}