【问题标题】:Shortest path algorithm in graph for queries查询图中的最短路径算法
【发布时间】:2022-01-20 13:35:37
【问题描述】:

我有一个加权无向图。它的顶点是两个集合的一部分 - S 和 T。首先,输入边。然后指定哪些顶点是 T 集的一部分(其余顶点是 S 集的一部分)。然后是 q 个查询。对于每个查询(由一个源顶点组成),程序必须打印指定的源顶点和集合 T 的任何顶点之间的最短路径。
我使用 Dijkstra 算法实现了该程序。我为源顶点上的每个查询调用它(dijkstra 返回源顶点与所有其他顶点之间的距离),然后返回这些数字中的最小值。

const int M = 1000000;
std::unordered_set<int> T;
class Node {
public:
    int endVertex;  // stores the second vertex of the edge
    int weight;     // stores the weight required, it is the weight of the edge
    Node(int end, int weight) {
        this->endVertex = end;
        this->weight = weight;
    }
};

struct NodeComparator {
    bool operator()(const Node &first, const Node &second) {
        return first.weight > second.weight;
    }
};


class Graph {
private:
    std::unordered_map<int, std::vector<Node>> adjacencyList; // it's a vector because there may be repeated Nodes
    int numberOfVertices;

    std::vector<int> dijkstra(int source) {
        std::priority_queue<Node, std::vector<Node>, NodeComparator> heap;
        std::vector<int> distances(this->numberOfVertices, M);
        std::unordered_set<int> visited;
        // distance source->source is 0
        distances[source] = 0;
        heap.emplace(source, 0);
        while (!heap.empty()) {
            int vertex = heap.top().endVertex;
            heap.pop();
            // to avoid repetition
            if (visited.find(vertex) != visited.end()) {
                continue;
            }
            for (Node node: adjacencyList[vertex]) {
                // relaxation
                if (distances[node.endVertex] > distances[vertex] + node.weight) {
                    distances[node.endVertex] = distances[vertex] + node.weight;
                    heap.emplace(node.endVertex, distances[node.endVertex]);
                }
            }
            // mark as visited to avoid going through the same vertex again
            visited.insert(vertex);
        }
        return distances;
    }

    int answer(int source) {
        std::vector<int> distances = this->dijkstra(source);
        std::set<int> answer;
        for (int i: T) {
            answer.insert(distances[i]);
        }
        return *answer.begin();
    }
// other methods
};
// main()

但是,由于超时,我的解决方案没有通过一半的测试。我用 Floyd-Warshall 算法替换了我的 dijkstra 方法,该算法直接覆盖了起始邻接矩阵,因为我认为该方法只会被调用一次,然后每个查询只会在矩阵的源行中找到最小元素。这次超时更严重。

是否有针对最短路径高效查询的特定算法?如何改进我的算法?

【问题讨论】:

  • 顺便说一下,将std::ios::sync_with_stdio(false); cin.tie(NULL); 添加到 main 不会以任何方式加速我的程序。
  • 你看到下面我的cmets了吗?你检查了吗?
  • @aropan 是的,我做到了。我上传了你的答案。然而,这不是解决方案。我现在正在添加它。

标签: c++ algorithm graph shortest-path dijkstra


【解决方案1】:

因此,事实证明问题需要以这样一种方式解决,即只计算一次结果,而不是像我之前那样为每个查询计算一次。 解决方案是添加一个假顶点,它连接到 T 集合中的所有顶点,并且每条边的权重为零。然后,你必须做std::vector&lt;int&gt; distances = graph.dijkstra(this fake edge);,每个查询的答案都是distances[query]

【讨论】:

  • 你是认真的吗?这和我建议的一样。而是添加假顶点,您可以从所有 T 顶点同时运行 Dijkstra。
  • 不过问题已经为你解决了就好了。
  • 对不起,我一定误解了你的回答。我会将您的标记为已接受。祝你圣诞快乐。
【解决方案2】:

这似乎没有必要

std::unordered_set<int> visited;

法线向量可以更快地完成这项工作

std::vector<int> visited(numberOfVertices,0);

所以你可以替换

if (visited.find(vertex) != visited.end()) {

速度更快

if( visited[vertex] )

还有

visited.insert(vertex);

变成

visited[vertex] = 1;

可以加快个别查询

替换

    std::set<int> answer;
    for (int i: T) {
        answer.insert(distances[i]);
    }
    return *answer.begin();

int shortest = MAX_INT;
for (int d: distances) {
   if( d < shortest ) {
      shortest = d;
   }
}
return shortest;

修改您的 dijsktra 代码以在路径到达 T 节点时停止搜索 - 无需进一步。

【讨论】:

  • 感谢您的回答。更换后,它会在相同的测试中超时。不幸的是,这还不够:-(。
  • 您的第二次编辑不起作用,因为distances 包含不属于 T 的顶点,包括源顶点本身。源与源之间的距离为 0。稍作更改后,为了纠正这个问题,测试仍然没有运行。似乎需要彻底改变逻辑。不过还是谢谢,
【解决方案3】:

您可以反转所有边并找到从 T 集合(从所有 T 顶点一起运行 Dijkstra)到某个顶点 S 的最短路径。并预先计算到每个 S 的所有距离并在 O(1) 中回答查询。

【讨论】:

    猜你喜欢
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多