4196 shortest path (heap optimized dijkstras algorithm template)

Posted by johnwayne77 on Thu, 10 Mar 2022 10:27:14 +0100

1. Problem Description:

Given an undirected graph with n points and m edges. Points are numbered from 1 to n. The graph may contain double edges and self rings. Please find and output the shortest path from point 1 to point n.

Input format

The first line contains two integers n, m. The next m lines contain three integers a, b and W, indicating that there is an undirected edge between point a and point b, and the edge length is w.

Output format

If the shortest path does not exist, − 1 is output. Otherwise, the number of points contained in the shortest path from point 1 to point n is output in one line, and each point number is separated by a space. If the answer is not unique, any reasonable scheme can be output.

Data range

The first six test points meet 2 ≤ n ≤ 10 and 1 ≤ m ≤ 10.
All test points meet 2 ≤ n ≤ 10 ^ 5, 1 ≤ m ≤ 10 ^ 5, 1 ≤ a, b ≤ n, 1 ≤ w ≤ 10 ^ 6.
This problem is not based on spfa algorithm.

Input sample:

5 6
1 2 2
2 5 5
2 3 4
1 4 1
4 3 3
3 5 1

Output example:

1 4 3 5
Source: https://www.acwing.com/problem/content/description/4199/

2. Train of thought analysis:

By analyzing the problem, we can know that this problem is the bare problem of the single source shortest path. For the problem of the single source shortest path, we first need to look at the data range and edge weight to determine which algorithm to use. Because the maximum n is 10 ^ 5, we can't use the O(n ^ 2) simple version of dijkstra algorithm to solve it. We can use the heap optimized version of dijkstra algorithm or spfa to solve it, Because this topic finally needs to output a shortest path from 1 to N, we need to record the points on the shortest path from 1 to N. here, we can use two methods to record. The first is to declare a prefix array pre, which updates the previous node reaching a certain point when solving the shortest distance from 1 to n, Finally, if there is a solution, we can recurs from the end point n to the starting point through the pre array; The second method: since the undirected edge is established, we can start from the end point and finally recurse to the starting point with the help of dis array.

3. The code is as follows:

Use a pre list to record the previous node of the node in the shortest path:

from typing import List
import heapq


class Solution:
    def dijkstra(self, n: int, s: int, t: int, g: List[List[int]]):
        # Because n is at most 10 ^ 5 and the maximum edge weight is 10 ^ 6, it should be declared larger
        INF = 10 ** 12
        dis = [INF] * (n + 10)
        dis[s] = 0
        vis = [0] * (n + 10)
        q = list()
        # Record the prefix of the node with the shortest path
        pre = [-1] * (n + 10)
        heapq.heappush(q, (0, s))
        while q:
            p = heapq.heappop(q)
            # If it has been accessed, skip it
            if vis[p[1]] == 1: continue
            # The tag has been accessed
            vis[p[1]] = 1
            for next in g[p[1]]:
                if dis[next[0]] > dis[p[1]] + next[1]:
                    # Update shortest distance
                    dis[next[0]] = dis[p[1]] + next[1]
                    # Update prefix node
                    pre[next[0]] = p[1]
                    heapq.heappush(q, (dis[next[0]], next[0]))
        # Judge whether there is no solution
        if pre[t] == -1: 
            print(-1)
            return 
        # Recursion from the end point to the starting point
        k = n
        path = list()
        while k != -1:
            path.append(k)
            k = pre[k]
        for i in range(len(path) - 1, -1, -1):
            print(path[i], end=" ")

    def process(self):
        n, m = map(int, input().split())
        # Undirected graph, so we need to establish edges in two directions
        g = [list() for i in range(n + 10)]
        for i in range(m):
            a, b, c = map(int, input().split())
            g[a].append((b, c))
            g[b].append((a, c))
        self.dijkstra(n, 1, n, g)


if __name__ == '__main__':
    Solution().process()

Because it is a two-way edge, you can recurs from the end point to the starting point:

from typing import List
import heapq


class Solution:
    def dijkstra(self, n: int, s: int, t: int, g: List[List[int]]):
        INF = 10 ** 12
        dis = [INF] * (n + 10)
        dis[s] = 0
        vis = [0] * (n + 10)
        q = list()
        # Introducing heapq module to operate the list is equivalent to simulating the operation of a heap
        heapq.heappush(q, (0, s))
        while q:
            p = heapq.heappop(q)
            # If it has been accessed, skip it
            if vis[p[1]] == 1: continue
            # The tag has been accessed
            vis[p[1]] = 1
            for next in g[p[1]]:
                if dis[next[0]] > dis[p[1]] + next[1]:
                    dis[next[0]] = dis[p[1]] + next[1]
                    heapq.heappush(q, (dis[next[0]], next[0]))
        if dis[t] == INF: 
            print(-1)
            return 
        res = [t]
        # Because it is a two-way edge, it can start at the end and reach the starting point recursively
        k = t
        while k != s:
            for next in g[k]:
                if dis[next[0]] + next[1] == dis[k]:
                    k = next[0]
                    res.append(k)
        # Flip it
        for i in range(len(res) - 1, -1, -1):
            print(res[i], end=" ")

    def process(self):
        n, m = map(int, input().split())
        # Undirected graph
        g = [list() for i in range(n + 10)]
        for i in range(m):
            a, b, c = map(int, input().split())
            g[a].append((b, c))
            g[b].append((a, c))
        return self.dijkstra(n, 1, n, g)


if __name__ == '__main__':
    Solution().process()

Topics: Algorithm