55、【图】Dijkstra求最短路径(单源最短路径+边权重为正数)(C/C++版)

算法介绍

Dijkstra算法是基于贪心思想,用于实现只含有正权边的单源最短路径问题,单源路径是从一个固定起始点出发到其余各点的路径。

算法过程:

首先,获取起始点到其余点的距离,然后从起始点出发,每次选取一个距离起始点最近的节点X作为中转点,比较通过中转点X到达其余路径直接到达其余路径,哪种情况最短并将最短的情况进行更新后,再选取除X以外另一个到起始点最近的点。以此方式,逐层递进,到达终点。

题目描述

给定一个 n 个点 m条边的有向图,图中可能存在重边和自环,所有边权均为正值。

请你求出 1号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。

输入格式

第一行包含整数 n和 m。
接下来 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是基于贪心思想的算法,算法过程:

首先,确定一个起始点,将各顶点到起始点的距离初始化为无穷大。然后再将与初始点相邻的其余点依次进行对比,选择距离初始点最近的一个点作为中转点(第一次选取的点为起始点自身,之后将会选取其余点作为中转点)。

然后,以该中转点再来探索与其相邻的点j 的距离,当起始点到点j 的距离小于起始点通过中转点再到点j 的距离,则将后者作为优选路径,完成全部邻接点的更新。

在这里插入图片描述
以此类推,依次逐步向目标点延伸,找到最短路径。

具体图示过程可参考:AcWing 849. Dijkstra求最短路 I:图解 详细代码

在表示无穷大时使用0x3f3f3f3f,在表示无穷小时使用0xc0c0c0c0

参考文章:
使用0x3f3f3f3f表示无穷大

c语言正负无穷的设置

算法实现

#include <stdio.h>

const int N = 510, INF = 0x3f3f3f3f;  // INF:无穷大
int n, m;
int g[N][N], dist[N];       // 邻接矩阵g和最短距离数组dist
//int pre[N];               // 构建前驱数组,记录最短路径序列中各节点的直接前驱
bool visit[N];              // 访问记录数组visit

int dijkstra(){
    for(int i = 0; i <= n; i++)     dist[i] = INF;     // 初始化为无穷大
    dist[1] = 0;        // 编号为1的结点到自身的距离

    for(int i = 0; i < n; i++){                      // 每遍历一次向前走一步,一共n个点遍历n次
        int t = 0;                                   // 存储访问点
        for(int j = 1; j <= n; j++){                 // 选取中转点
            if(!visit[j] && dist[j] < dist[t])      t = j;  // 每次选取距离原点最近的点作为中转点
        }
        visit[t] = true;                            // 标记已被访问过

        for(int j = 1; j <= n; j++){                // 更新原点0至各点的距离
            if(dist[t] + g[t][j] < dist[j]){        // 判断经过之前的路径近还是经过中间点t近
                dist[j] = dist[t] + g[t][j];
                //pre[j] = t;                       // 记录到达结点j的上一个结点为t
            }
        }
    }

    // if(dist[n] == INF)          // 若到n为无穷大,说明不存在由结点1到n的路径
    //     return -1;  
    // else
    //     return dist[n];         // 返回最短路径

    return dist[n] == INF ? -1 : dist[n];
}


int main(){
    scanf("%d%d", &n ,&m);
    for(int i = 0; i <=n; i++)          // 初始化邻接矩阵g为无穷大
        for(int j = 0; j <= n; j++)
           if(i != j)   g[i][j] = INF;

    while(m--){
        int x, y, z;        scanf("%d%d%d", &x, &y ,&z);
        if(g[x][y] > z)     g[x][y] = z;        // 选取权重最小的边
    }
    printf("%d", dijkstra());

    // 输出最短路径
    //puts("");                                 
    //for(int i = n; i != 0; i = pre[i])       printf("%d ", i);

    return 0;
}

时间复杂是 O(n2+m), n 表示点数,m 表示边数。

无注释代码

#include <stdio.h>

const int N = 510, INF = 0x3f3f3f3f;
int n, m;
int g[N][N], dist[N];
bool visit[N];

int dijkstra(){
    for(int i = 0; i <= n; i++)     dist[i] = INF;
    dist[1] = 0;
    
    for(int i = 0; i < n; i++){
        int t = 0;
        for(int j = 1; j <= n; j++){
            if(!visit[j] && dist[j] < dist[t])      t = j;
        }
        visit[t] = true;
        
        for(int j = 1; j <= n; j++){
            if(dist[t] + g[t][j] < dist[j])         dist[j] = dist[t] + g[t][j];
        }
    }
    
    return dist[n] == INF ? -1 : dist[n];
}

int main(){
    scanf("%d%d", &n, &m);
    for(int i =0; i <= n; i++)
        for(int j = 0; j <= n; j++)
           if(i != j)   g[i][j] = INF;
            
    while(m--){
        int x, y, z;        scanf("%d%d%d", &x, &y, &z);
        if(g[x][y] > z)     g[x][y] = z;
    }
    printf("%d", dijkstra());
    
    return 0;
}

参考资料:
8.11 最短路 Dijkstra 算法——信息学奥赛培训课程

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰阳星宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值