图最短路径Dijkstra - 时间优化

问题描述 投票:0回答:1

所以我正在继续我的算法课程

输入数据 输入数据的第一行包含两个数字:𝑛 (2≤𝑛≤2⋅10^5) 和 𝑚 (1≤𝑚≤4⋅10^5) — 帝国的定居点数量和帝国的道路数量它。

以下𝑚线描述了定居点之间的道路。每条道路由三个数字描述:𝑣、𝑢 (1≤𝑣,𝑢,≤𝑛,𝑣≠𝑢) 和 𝑤 (1≤𝑤≤10^9)。这意味着在数字 𝑣 和 𝑢 的点之间有一条长度为 𝑤 的道路。

输出 输出文件的第一行必须包含一个自然数 𝑘 (2≤𝑘≤𝑛) — 从定居点 1 到定居点 𝑛 的最短路径上的定居点数量。

在第二行,以空格分隔,按遍历顺序打印从 1 到 𝑛 的最短路径上的聚落,包括 1 和 𝑛 本身。

如果没有从1到𝑛的路径,则输出−1。在这种情况下,你可能会开始担心车夫的生活了。

如果有多个答案,请打印其中一个。

测试时间限制:1秒 每个测试的内存限制:256 MB

import heapq
import sys

def dijkstra(graph, start, end):
    n = len(graph)
    distances = [float('inf')] * n
    distances[start] = 0
    prev = [-1] * n
    pq = [(0, start)]

    while pq:
        dist, u = heapq.heappop(pq)
        if u == end:
            path = [end]
            while prev[path[-1]] != -1:
                path.append(prev[path[-1]])
            path.reverse()
            return dist, path
        
        for v, w in graph[u]:
            if dist + w < distances[v]:
                distances[v] = dist + w
                prev[v] = u
                heapq.heappush(pq, (distances[v], v))

    return -1, []

#input data
n, m = map(int, sys.stdin.readline().split())
graph = [[] for _ in range(n)]
for _ in range(m):
    v, u, w = map(int, sys.stdin.readline().split())
    graph[v-1].append((u-1, w))
    graph[u-1].append((v-1, w))

# Dijkstra func
distance, path = dijkstra(graph, 0, n-1)

# Outout
if distance == -1:
    sys.stdout.write("-1\n")
else:
    sys.stdout.write(str(len(path)) + "\n")
    sys.stdout.write(" ".join(str(x+1) for x in path) + "\n")

该算法工作正常,但我遇到时间限制问题。 我已经进行了一项优化,其运行速度比一开始的优化快 2 倍,但未来还有更多优化。

我研究了大约10天,并且已经阅读了很多关于这个主题的文章,但要么我不明白如何实施,要么它只是优化

我选择Dijkstra算法作为解决此类问题的经典方法。

在研究了实现方式之后,我选择堆作为最快的推荐。 还尝试了其他方法来收集路径。

还将输入输出更改为 sys.stdout,并且在速度上有一些好处,但仍然不够 * 也许有更好的算法来完成他的任务?还是只是优化不当的问题? *

输入 5 5 1 2 1 2 3 6 3 4 7 4 5 10 1 4 3 1 3 4

输出3 1 4 5

输入

8 9 1 2 4 2 3 6 3 1 5 1 4 5 1 5 5 5 4 7 5 6 1 6 7 1 7 8 1

输出

5 1 5 6 7 8

输入 5 4 1 2 5 1 3 5 2 3 1 4 5 1

输出

python algorithm shortest-path dijkstra
1个回答
0
投票

由于我们没有任何东西可以测试,这将是在黑暗中进行的尝试,但是如果您使用双向 Dijkstra 搜索,您可以获得一些运行时间增益。

以下是该想法的可能实现:

from heapq import heappop, heappush
from dataclasses import dataclass

@dataclass
class ShortestPath:
    distance = float("inf")
    node_on_path = None
        

class DijkstraState:
    def __init__(self, graph, start, shortest: ShortestPath):
        n = len(graph)
        self.graph = graph
        self.distances = [float('inf')] * n
        self.distances[start] = 0
        self.prev = [-1] * n
        self.pq = [(0, start)]
        self.opposite_distances = []
        self.shortest = shortest
        
    def process_next(self):
        dist, node = heappop(self.pq)
        if dist == self.distances[node]:
            for neighbor, weight in self.graph[node]:
                neighbor_dist = dist + weight
                if neighbor_dist < self.distances[neighbor]:
                    self.distances[neighbor] = neighbor_dist
                    self.prev[neighbor] = node
                    heappush(self.pq, (neighbor_dist, neighbor))
                    total_distance = neighbor_dist + self.opposite_distances[neighbor]
                    if total_distance < self.shortest.distance:
                        self.shortest.distance = total_distance
                        self.shortest.node_on_path = neighbor
        return dist

    def get_path_to_start(self):
        node = self.shortest.node_on_path
        while node != -1:
            yield node
            node = self.prev[node]


def dijkstra(graph, start, end):
    if start == end:  # Boundary case
        return [start]
    shortest = ShortestPath()  # mutable object to record shortest of inspected paths
    forward = DijkstraState(graph, start, shortest)
    backward = DijkstraState(graph, end, shortest)
    # Each gets a reference the distances found in the other direction
    forward.opposite_distances = backward.distances
    backward.opposite_distances = forward.distances
    
    while forward.pq and backward.pq:
        distance = forward.process_next() + backward.process_next()
        if distance >= shortest.distance:  # Sure to have found the shortest path
            path = list(forward.get_path_to_start())[:0:-1]
            path.extend(backward.get_path_to_start())
            return path

    return []  # No path found

注意:如您所见,这不会返回距离。我认为你不需要它。要知道何时输出-1,只需查看返回的路径为空即可:

# ... 
# ...

# Dijkstra func
path = dijkstra(graph, 0, n-1)

# Outout
if not path:
    print("-1")
else:
    print(len(path))
    print(" ".join(str(x+1) for x in path))
© www.soinside.com 2019 - 2024. All rights reserved.