最短路问题中的朴素版Dijkstra算法

最短路问题的朴素版Dijkstra算法

最短路问题需要用到下面的算法(n代表点的数量,m代表边的数量)
在这里插入图片描述
朴素版和堆优化版的Dijkstra算法的区别是,朴素版比较适合稠密图,堆优化版适合稀疏图,稠密图代表它的边比较多,边的数量最多是点的平方,稀疏图代表边基本于点的数量差不多。


对于下面的这个题,就需要用到朴素版的Dijkstra算法。

题目

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

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

输入格式

第一行包含整数 n n n m m m

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

输出格式

输出一个整数,表示 1 1 1 号点到 n n n 号点的最短距离。

如果路径不存在,则输出 − 1 -1 1

数据范围

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

输入样例:

3 3
1 2 2
2 3 1
1 3 4

输出样例:

3

朴素版的Dijkstra思路如下:

  1. 将所有点到1号点的距离设置为正无穷(一个很大的数,当做正无穷看待,并不是真正的正无穷),将1号点的距离设置成0。
  2. 首先外层遍历 n 次循环
  3. 每次循环找到一个集合外的点当中距离离1最近的点,假设是 t
  4. 把该点放到集合当中
  5. 利用t更新 与 t 连接的点

在这里插入图片描述

存储稠密图需要用到邻接矩阵存储,g[a] [b] 代表 a点 到 b 点的距离。

数组 dist 用来存储n号点到 1号点的距离,st 代表该点是在集合内还是集合外。

一般建议每次将题目的点的数量写成N,将边写成 M,并且尽量在原有最大数据上 加一点数字,我这里都多了10,是为了防止出现数组不够的情况


首先是 输入环节,开始将整个图存起来
在这里插入图片描述
由于题目当中会含有重边,所以我们需要 2 号语句,只算最小的那条边,由于这样最开始的 二维数组未初始化,所以值都是0,所以我们需要用 1 号语句,将所有值变为 正无穷大,这样就不会求最小值是0了,而且写1号语句的也不仅如此,还关系到点的更新,所以必须进行初始化

我们可以用 0x3f3f3f3f 当做无穷大,这个数作为无穷大的好处是 乘以2之后不会溢出 int 的范围,并且该数字很大。
在这里插入图片描述
memset 里面的是 0x3f 的原因是 memset 设置的是每个字节的值,所以每个字节是 0x3f,int 类型是4个字节,所以就是 0x3f3f3f3f。

在这里插入图片描述
dijkstra函数执行完之后,数组 dist[ n ]存的就是 1号点到 n 号点的距离。

在dijkstra 函数内部我们会将 dist数组当中所有的值设置成 正无穷大,也就是0x3f3f3f3f,所以如果当dist[n]还是正无穷大时,也就证明1号点走不到 n 号点,没有点更新 n号点。

根据题目要求走不到则打印 -1

在这里插入图片描述
在dijkstra函数中,就需要用到刚才的思路。

第一步,将数组 dist 中的值 设置为正无穷大(跟刚才设置数组g 一样),将1号点的距离设为1。

接着外层for循环 到 n
在这里插入图片描述
在循环内部有 三个步骤。

  1. 每次找到所有集合外的点当中距离最近的点
    在这里插入图片描述
    变量 t 为每次最近的点,遍历一遍所有的点,!st[j] 代表在集合外,如果当 t 还是 -1 或 当 t 这个点的距离遇到更小的距离的时候,则将 j 赋值给 t。

    此时 t 点就是距离最近的点

  2. 第二步 将该点放到集合当中,代表着它的距离已经确定了。

在这里插入图片描述
数组 st 的含义就是代表该点是否在集合里,所以只需要将 t 点设为 true 即可

  1. 第三步,利用 t 点更新其他可以更新的点

在这里插入图片描述
这里也解释了为什么 数组 g 要设置成正无穷大,正无穷大的含义相当于 t 点 与 j 点没有直接练在一起。

所以如果没有连在一起,那么也就不会更新。

也就是说,这一步,遍历了所有的点,看一看能不能 用 t 更新它,如果 1号点到 t点 的距离加上 t到 j 的距离 小于1号点到 j点的距离,那么就会更新。

在这里插入图片描述
比如这里原来 j 号点到 1 的距离是 2 + 4 = 6,此时 t 到1的距离为3,加上 t 到 j 的距离 1 为 4,因为小于6,所以就会更新 j 到 1 的距离。


最后还可以进行一点小小的优化,不优化也没任何问题
在这里插入图片描述
更新 n - 1 次的时候, n 号点 就必定会被更新,当然前提能到达的情况下,因为每次都找一个集合外最近的点。

当找到 最近的点为 n 时,说明此时 n 号点的距离就已经确定了,所以可以直接break出去。


完整代码如下:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 510, M = 1e5+10;

int g[N][N];
int dist[N];
bool st[N];

int n, m;

void dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    
    for (int i = 0; i < n - 1; i++)
    {
        //1.找到距离最近的点
        int t = -1;
        for (int j = 1; j <= n; j++)
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
        
        if (t == n) break;
                
        //2.将距离最近的点放到集合当中
        st[t] = true;
        
        //3.更新点
        for (int j = 1; j <= n; j++)
            if (!st[j])
                dist[j] = min(dist[j], dist[t] + g[t][j]);
                
    }
}


int main()
{
    scanf("%d%d", &n, &m);
    memset(g, 0x3f, sizeof g);//1
    for (int i = 0; i < m; i++)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        g[a][b] = min(g[a][b], c);//2
    }

    dijkstra();
    
    if (dist[n] == 0x3f3f3f3f) puts("-1");
    else printf("%d\n", dist[n]);
    
    return 0;
}



  • 18
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值