ACWing - Dijkstra求最短路

340 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目描述:

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

输入格式

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

输出格式

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

数据范围

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

输入样例:

3 3
1 2 2
2 3 1
1 3 4

输出样例:

3

二、思路分析:

​ 这一题我们需要使用朴素Dijkstra算法,优先使用朴素Dijkstra算法的特点有

  1. 是单源最短路问题。(起点确定,且只有一个)
  2. 边的权重都是正数。
  3. 抽象出来的图为稠密图。

朴素Dijkstra算法的使用步骤

  1. 初始化距离 :dis[1] = 0,其他点的距离为正无穷
  2. 开始循环 n - 1次(每次都会确定一个点的最短距离)
    1. 确定一个目前最短距离的点且没有被放进为最短距离的数组点
    2. 以这个点为标准更新每个点的距离
  3. 输出最后目标点的距离

三、AC 代码:

import java.util.Scanner;

/** 
 * 注意 Integer.MAX_VALUE 加上任意一个整数都会照成数据溢出
 */
public class acwing_849{
    /** 图的点数 N  图的边数 M */
    static int N,M;
    /** 邻接矩阵(存放稠密图) */
    static int[][] g = null;
    /** 点的距离 */
    static int[] dis = null;
    /** 已确定点的集合 */
    static boolean[] s = null;
  
    public static void main(String[] args) {
        
        Scanner in = new Scanner(System.in);
        /** 初始化数据 */
        N = in.nextInt(); M = in.nextInt();
        g = new int[ N + 1 ][ N + 1 ];
        dis = new int[ N + 1 ];
        s = new boolean[N + 1];
        
        for (int i = 1; i < N + 1; i++) {
            for (int j = 1; j < N + 1; j++) {
                g[i][j] = 10001;
            }
        }
        for (int i = 0; i < M; i++) {
            int a = in.nextInt(); int b = in.nextInt();
            g[a][b] = Math.min(in.nextInt(), g[a][b]);
        }

        dis[1] = 0;
        for (int i = 2; i < N + 1; i++) {
            dis[i] = 10001;
        }

        System.out.println(Dijkstra());
    }

    static int Dijkstra(){
        /** 遍历 n - 1 次 ,确定 n - 1 个节点。因为起点已经确定了*/
        for (int i = 0; i < N - 1; i++) {

            /** t是距离最短的节点,默认要为不存在的下标 */
            int t = -1;
            
            /** 找到最短距离且不在已经确定的节点里 */
            for (int j = 1; j < N + 1; j++) {
                if (!s[j] && (t == -1 || dis[t] > dis[j])) {
                    t = j;
                }
            }

            /** 利用这个节点来更新距离 */
            for (int j = 1; j < N + 1; j++) {
                dis[j] = Math.min(dis[j], dis[t] + g[t][j]);
            }

            /** 把节点放到标志数组里 */
            s[t] = true;
        }
        if(dis[N] == 10001){
            return -1;
        }     
        return dis[N];    
    }
}