849 Dijkstra求最短路 I(朴素Dijkstra算法模板)

1. 问题描述:

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。

输入格式

第一行包含整数 n 和 m。接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式

输出一个整数,表示 1 号点到 n 号点的最短距离。如果路径不存在,则输出 −1。

数据范围

1 ≤ n ≤ 500,
1 ≤ m ≤ 10 ^ 5,
图中涉及边长均不超过10000。

输入样例:

3 3
1 2 2
2 3 1
1 3 4

输出样例:

3
来源:https://www.acwing.com/problem/content/description/851/

2. 思路分析:

这道题目属于单源最短路径的裸题,因为边的权重都是正数,所以可以使用dijkstra算法解决(只要是非负无环图都可以使用dijkstra实验法求解),dijkstra算法用来求解从起点s到终点t的单源最短路径,因为n = 500,所以使用朴素版的dijkstra算法也是可以通过的,时间复杂度为O(n ^ 2) = 2.5 * 10 ^ 4,直接默写模板即可。因为使用的python语言,所以在建图的时候使用g = [dict() for i in range(n + 1)],每一个g[i]其实是一个字典,这样可以很容易找到某条边的起点,终点以及对应的权重,第二种比较常用的建图方式是可以声明g中的每一个元素为一个列表,也即[list() for i in range(n + 1)];这两种邻接表存储图的形式都是可以的。dijkstra算法需要注意的一个问题是题目中节点的下标是从0还是从1开始的,有的时候没有注意的时候就写错了。

3. 代码如下:

每一个g[i]是一个字典:

from typing import List


class Solution:
    # s表示起点, t表示终点, n表示点的个数, g为有向图
    def dijkstra(self, s: int, t: int, n: int, g: List[dict]):
        INF = 10 ** 10
        dist = [INF] * (n + 1)
        # 自己到自己的距离为0
        dist[s] = 0
        vis = [0] * (n + 1)
        # 其实循环到n - 1就可以了最后一个点对结果没有什么影响可以循环也可以不循环
        for i in range(n):
            k = -1
            # 注意节点的下标是从1开始的, 这是一个坑
            for j in range(1, n + 1):
                # 找到当前从起点到其余点的最短距离的点的编号
                if vis[j] == 0 and (k == -1 or dist[k] > dist[j]):
                    k = j
            vis[k] = 1
            for j in range(1, n + 1):
                # 判断以当前的中点k是否有出边, 如果有出边并且以当前的k作为中间点到达的距离更小那么就更新最短距离
                if vis[j] == 0 and j in g[k] and dist[k] + g[k][j] < dist[j]:
                    dist[j] = dist[k] + g[k][j]
        return dist[t] if dist[t] != INF else -1

    # dijkstra算法适用于求解边权为非负的图, 时间复杂度为O(n ^ 2)
    def process(self):
        n, m = map(int, input().split())
        # 每一个g[i]都是一个字典这样后面在找中间点k的权重的时候比较方便
        g = [dict() for i in range(n + 1)]
        for i in range(m):
            x, y, z = map(int, input().split())
            # 有向图, 下面的min是为了在重边的时候取一条较短的边
            if y in g[x]: g[x][y] = min(g[x][y], z)
            else: g[x][y] = z
        return self.dijkstra(1, n, n, g)


if __name__ == "__main__":
    print(Solution().process())

每一个g[i]是一个列表:

from typing import List


class Solution:
    def dijkstra(self, s: int, t: int, n: int, g: List[List[int]]):
        INF = 10 ** 10
        dis = [INF] * (n + 1)
        dis[s] = 0
        vis = [0] * (n + 1)
        for i in range(1, n + 1):
            k = -1
            for j in range(1, n + 1):
                if vis[j] == 0 and (k == - 1 or dis[j] < dis[k]):
                    k = j
            vis[k] = 1
            # 遍历k的邻接点
            for next in g[k]:
                if dis[next[0]] > dis[k] + next[1]:
                    dis[next[0]] = dis[k] + next[1]
        return dis[t] if dis[t] != INF else -1

    def process(self):
        n, m = map(int, input().split())
        g = [list() for i in range(n + 1)]
        for i in range(m):
            x, y, z = map(int, input().split())
            g[x].append((y, z))
        return self.dijkstra(1, n, n, g)


if __name__ == "__main__":
    print(Solution().process())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值