45.Acwing基础课第849题-简单-Dijkstra求最短路 I

45.Acwing基础课第849题-简单-Dijkstra求最短路 I

题目描述

给定一个 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

输入样例:

3 3
1 2 2
2 3 1
1 3 4

输出样例:

3

代码:

#include<iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 510;
const int INF = 0x3f3f3f3f;
int n, m;//图的节点个数和边数
int g[N][N];//邻接矩阵
int dist[N];
bool st[N];

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);//dist 数组的各个元素为无穷大
    dist[1] = 0;//源点到源点的距离为置为 0
    
    for (int i = 0; i < n; i++)//迭代n次
    {
        int t = -1;
        //找到每个结点与其直接连接的结点之间的距离
        for (int j = 1; j <= n; j++)//遍历 dist 数组,找到没有确定最短路径的节点中距离源点最近的点t
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
                
        if(t == n) break;
        
        st[t] = true;
        
        //用g[N][N]更新每个结点与起始结点之间距离
        for (int j = 1; j <= n; j++)
             dist[j] = min(dist[j], dist[t] + g[t][j]);
    }
    
    // 3. 处理无解情况(起点到n号点不可达)
    return dist[n] == INF ? -1 : dist[n];
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(g, 0x3f, sizeof g);//邻接矩阵初始化

    while (m--)//读入 m 条边
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        g[a][b] = min(g[a][b], w);
    }

    int t = dijkstra();
    
    printf("%d\n", t);
    
    return 0;
}

为什么前面初始化的是3f,后面变成检查3f3f33f3f了?

memset 是按字节更改内存,memset(a, 0x3f, sizeof a): a 的每一个字节都是 0x3f, int 取4个字节,所以是 0x3f3f3f3f,他的值是0x3f3f3f3f

在图论的算法实现中,尤其是最短路径算法,经常会见到使用 memset 函数将数组元素初始化为一个很大的值,这里用的是 0x3f,是出于以下几个原因:

1.表示无穷大

  • 0x3f3f3f3f 这个数在十进制下约为 10^9,这个值足够大,可以被认为是“无穷大”;
  • 在大多数有权图的最短路径问题中,边权的总和不会超过这个数,因此 0x3f3f3f3f 可以作为初始的“没有路径”的表示。

2.便利的十六进制表示法

  • 由于 0x3f 是一个十六进制的数,在内存中表示为 00111111,连续两个 0x3f 就是 00111111 00111111
  • 利用 memsetint 数组进行初始化,每个 int 是 4 个字节,连续四个 0x3f 就填充了一个 int
  • 这也使得 0x3f3f3f3f 成为了一个便于 memset 使用的值。

3.效率

  • memset 通常比循环赋值要高效,因为它是针对内存进行的快速操作。

4.特定值的比较

  • 在这个代码中,if(dist[n] == 0x3f3f3f3f) 用于判断顶点 n 到源点 (1 号点) 是否可达;
  • 如果 dist[n] 仍然保持被 memset 初始化的值未被更新,说明没有从源点到 n 的路径,因此返回 -1

总而言之,在这段代码中使用 0x3f 进行 memset 初始化,和检查 dist[n] 是否为 0x3f3f3f3f 是一个对内存友好而且高效的方法来初始化图的邻接矩阵和距离数组,并在算法结束时快速判断节点是否可达。

算法步骤

image-20240522201338435

个人总结:

dist[1]=0
1.外部for循环开始 i=0
(1)内部for循环1开始
①j=1时,t == -1,t=j=1;
②1<j<=5时,如果dist[t] > dist[j],j>t
第一次dist[1]是0 > dist[2]是∞ 不成立
第二次dist[1]是0 > dist[3]是∞ 不成立
第三次dist[1]是0 > dist[4]是∞ 不成立
第四次dist[1]是0 > dist[5]是∞ 不成立
③1 不等于 5;st[1] = true;(源点设置完成此后不再更新)
(2)内部for循环2开始 通过g[N][N]矩阵保存的参数更新源点到源点直接连接的结点之间的距离
①j=1时,dist[1] = min(dist[1], dist[1] + g[1][1]);(dist[1]=0)更新节点1-1距离
②j=2时,dist[2] = min(dist[2], dist[1] + g[1][2]);(dist[2]=100)更新节点1-2距离
③j=3时,dist[3] = min(dist[3], dist[1] + g[1][3]);(dist[3]=150)更新节点1-3距离
④j=4时,dist[4] = min(dist[4], dist[1] + g[1][4]);(dist[4]=140)更新节点1-4距离:比较[1-4结点距离]和[1号结点距离+1-4结点距离]大小
⑤j=5时,dist[5] = min(dist[5], dist[1] + g[1][5]);(g[1][5]=∞,dist[5]=∞)走不通
2.外部for循环 i=1
(1)内部for循环1开始
①j=2时,t == -1,t=j=2;
②2<j<=5时,如果dist[t] > dist[j],j>t
第一次dist[2] > dist[3]不成立
第二次dist[2] > dist[4]不成立
第三次dist[2] > dist[5]不成立
③2 不等于 5;st[2] = true;(节点2设置完成此后不再更新)
(2)内部for循环2开始 通过g[N][N]矩阵保存的参数更新节点2到节点2直接连接的结点之间的距离
①j=1时,dist[1] = min(dist[1], dist[2] + g[2][1]);(0<100+∞,dist[1]=0)
②j=2时,dist[2] = min(dist[2], dist[2] + g[2][2]);(100==100+0,dist[2]=100)
③j=3时,dist[3] = min(dist[3], dist[2] + g[2][3]);(150<100+∞,dist[3]=150)
④j=4时,dist[4] = min(dist[4], dist[2] + g[2][4]);(140<100+80,dist[4]=140)
⑤j=5时,dist[5] = min(dist[5], dist[2] + g[2][5]);(∞>100+80,dist[5]=180)
3.外部for循环 i=2
(1)内部for循环1开始
①j=3时,t == -1,t=j=3;
②3<j<=5时,如果dist[t] > dist[j],j>t
第一次dist[3] > dist[4]成立 t=4
第二次dist[4] > dist[5]不成立
③4 不等于 5;st[4] = true;(节点4设置完成此后不再更新)
(2)内部for循环2开始 通过g[N][N]矩阵保存的参数更新节点4到节点4直接连接的结点之间的距离
①j=1时,dist[1] = min(dist[1], dist[4] + g[4][1]);(0<140+∞,dist[1]=0)
②j=2时,dist[2] = min(dist[2], dist[4] + g[4][2]);(100<140+∞,dist[2]=100)
③j=3时,dist[3] = min(dist[3], dist[4] + g[4][3]);(150<140+∞,dist[3]=150)
④j=4时,dist[4] = min(dist[4], dist[4] + g[4][4]);(140==140+0,dist[4]=140)
⑤j=5时,dist[5] = min(dist[5], dist[4] + g[4][5]);(180<140+10,dist[5]=150)
4.外部for循环 i=3
(1)内部for循环1开始
①j=3时,t == -1,t=j=3;
②4<j<=5时,如果dist[t] > dist[j],j>t
第一次dist[3] > dist[5]不成立
③3 不等于 5;st[3] = true;(节点3设置完成此后不再更新)
(2)内部for循环2开始 通过g[N][N]矩阵保存的参数更新节点3到节点3直接连接的结点之间的距离
①j=1时,dist[1] = min(dist[1], dist[3] + g[3][1]);(0<150+∞,dist[1]=0)
②j=2时,dist[2] = min(dist[2], dist[3] + g[3][2]);(100<150+∞,dist[2]=100)
③j=3时,dist[3] = min(dist[3], dist[3] + g[3][3]);(150==150+0,dist[3]=150)
④j=4时,dist[4] = min(dist[4], dist[3] + g[3][4]);(140<150+15,dist[4]=140)
⑤j=5时,dist[5] = min(dist[5], dist[3] + g[3][5]);(150<150+∞,dist[5]=150)
5.外部for循环 i=4
(1)内部for循环1开始
①j=5时,t == -1,t=j=5;
如果dist[t] > dist[j],j>t
第一次dist[5] > dist[5]不成立
②5 等于 5;

求源点到其余各点的最短距离步骤如下:

1.用一个 dist 数组保存源点到其余各个节点的距离,dist[i] 表示源点到节点 i 的距离。初始时,dist 数组的各个元素为无穷大。
用一个状态数组 state 记录是否找到了源点到该节点的最短距离,state[i] 如果为真,则表示找到了源点到节点 i 的最短距离,state[i] 如果为假,则表示源点到节点 i 的最短距离还没有找到。初始时,state 各个元素为假。

03.png

2.源点到源点的距离为 0。即dist[1] = 0。

04.png

3.遍历 dist 数组,找到一个节点,这个节点是:没有确定最短路径的节点中距离源点最近的点。假设该节点编号为 i。此时就找到了源点到该节点的最短距离,state[i] 置为 1。

05.png

4.遍历 i 所有可以到达的节点 j,如果 dist[j] 大于 dist[i] 加上 i -> j 的距离,即 dist[j] > dist[i] + w[i][j](w[i][j] 为 i -> j 的距离) ,则更新 dist[j] = dist[i] + w[i][j]。

06.png

5.重复 3 4 步骤,直到所有节点的状态都被置为 1。

28.Acwing基础课第849题-简单-Dijkstra求最短路 I6

参考资料:

[AcWing 849. Dijkstra求最短路 I:图解 详细代码(图解)](AcWing 849. Dijkstra求最短路 I:图解 详细代码(图解) - AcWing)

posted @ 2026-04-08 15:00  CodeMagicianT  阅读(1)  评论(0)    收藏  举报