📜  门|门CS 2008 |问题 28(1)

📅  最后修改于: 2023-12-03 15:42:22.380000             🧑  作者: Mango

题目介绍

该题目为《门|门CS 2008》第28题,是一道经典的图论题目。该题目考察了最短路算法,需要使用Dijkstra算法或者SPFA算法求解。

题目描述

给定一个N个点,M条边的有向图,每条边有一个权值,求从源点到汇点的最短路径长度。如果不存在从源点到汇点的路径,则输出-1。

输入格式

第一行包含三个整数N,M,S,其中S表示源点。

接下来M行,每行包含三个整数a,b,c,表示存在一条从a到b的有向边,边权为c。

输出格式

输出一个整数,表示从源点到汇点的最短路径长度。如果不存在从源点到汇点的路径,则输出-1。

算法思路

本题可以使用Dijkstra算法或者SPFA算法求解最短路径长度。这里以Dijkstra算法为例进行介绍。

Dijkstra算法是一种贪心算法,用来求解有向图中单源最短路径的问题。该算法维护了一个集合S,表示已经求得最短路径的结点集合。算法的思想是:从源点开始不断扩展S,直到扩展到汇点或者无法再扩展为止。

算法流程如下:

  1. 初始化:设源点为s,将所有结点的最短路径长度初始化为inf,将源点的最短路径长度初始化为0。将所有点标记为未访问。

  2. 迭代:从未访问的结点中,选择距离源点最近的结点u,将其标记为已访问,并尝试更新其它结点的最短路径。

  3. 更新:对于u的所有未访问的邻居结点v,如果源点到v的最短路径大于源点到u的最短路径加上(u,v)的边权,则更新源点到v的最短路径长度。

  4. 终止条件:如果终点已经被访问,表示已经找到最短路径并返回其长度;如果未访问的结点中没有结点距离源点小于inf,则表示不存在从源点到终点的路径。

代码实现

from collections import defaultdict
import heapq

def dijkstra(graph, start_node, end_node):
    # 初始化最短路径长度
    dist = {node: float('inf') for node in graph}
    dist[start_node] = 0
    
    # 初始化堆,将起点加入堆
    heap = [(0, start_node)]
    
    # 初始化父结点
    parent = defaultdict(list)
    
    # Dijkstra算法开始
    while heap:
        # 取出堆顶结点
        (cost, current_node) = heapq.heappop(heap)
        
        # 如果当前结点已经处理过,直接跳过
        if cost > dist[current_node]:
            continue
            
        # 遍历当前结点的邻居结点
        for neighbor, weight in graph[current_node].items():
            # 更新邻居结点的最短路径长度
            if dist[current_node] + weight < dist[neighbor]:
                dist[neighbor] = dist[current_node] + weight
                parent[neighbor] = [current_node]
                heapq.heappush(heap, (dist[neighbor], neighbor))
            elif dist[current_node] + weight == dist[neighbor]:
                parent[neighbor].append(current_node)
            
            # 检查是否到达终点
            if neighbor == end_node:
                return (dist[end_node], parent)
    
    # 如果到达这里,表示不存在从源点到终点的路径
    return (-1, None)

# 样例输入
n, m, s = 5, 7, 1
edges = [(1, 2, 2), (1, 3, 3), (2, 4, 4), (2, 5, 5), (3, 4, 1), (3, 5, 6), (4, 5, 2)]

# 构建邻接表
graph = defaultdict(dict)
for a, b, c in edges:
    graph[a][b] = c
    
# 运行Dijkstra算法
dist, parent = dijkstra(graph, s, n)

# 输出结果
if dist == -1:
    print("-1")
else:
    print(dist)

代码中使用了一个辅助数组parent,用来记录每个结点的父结点。如果存在多条最短路径,需要记录所有的父结点,因此使用了一个list来存储父结点。