AcWing 850. Dijkstra求最短路 II

题目

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

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

输入格式

第一行包含整数 \(n\)\(m\)

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

输出格式

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

如果路径不存在,则输出 \(-1\)

数据范围

\(1 \le n,m \le 1.5 \times 10^5\),
图中涉及边长均不小于 \(0\),且不超过 \(10000\)
数据保证:如果最短路存在,则最短路的长度不超过 \(10^9\)

输入样例:

3 3
1 2 2
2 3 1
1 3 4

输出样例:

3

题解

C++ 代码

#include <iostream>
#include <cstring>
#include <queue> //使用库中的priority_queue来模拟小根堆
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;

const int N = 1e6 + 10;

int n, m;
int h[N], w[N], e[N], ne[N], idx; //利用邻接表来模拟堆,w[N]用来存储每条边的权值,也就是两点之间的距离
int dist[N];
bool st[N];

void add(int a, int b, int c) //有重边也不要紧,输入一次存储一次,idx指针会自加,最后都会存在a号点的链表下,那怎么解决重边 重复存储的问题呢 看[1]位置
{
    e[idx] = b;
    ne[idx] = h[a];
    w[idx] = c; //idx指针对应在w[]内存储a到b的距离,此时的idx指针是在a号点的链表下 指向b号点 的指针 注意!这行一定要写在h[a] = idx ++ 的前面,要不然存储w[]的时候idx已经自加了,就会导致错误
    h[a] = idx ++ ;
}
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap; //将默认的大根堆转换为需要的小根堆的定义方式
    heap.push({0, 1}); //存入节点到源点的距离 和对应的节点编号 小根堆中默认按照pair中第一个元素进行排序,所以我们将距离放在heap.first存储

    while (heap.size())
    {
        auto t = heap.top(); //取出堆顶,即取出d堆内距源点距离最小的点
        heap.pop(); //释放该点
        int ver = t.second, distance = t.first; //ver存节点编号 distance存距离
        if (st[ver]) continue; //如果该点已经被访问过,退出本次循环 【1】
        st[ver] = true; //该点还未被访问过 就打上标记

        for (int i = h[ver]; i != -1; i = ne[i]) //从第ver号点开始遍历ver号点下的邻接表
        {
            int j = e[i]; //取出ver号点所连的j号点
            if (dist[j] > dist[ver] + w[i]) //如果ver号点到源点距离加上ver到j的距离 小于 j号点本来到原点的距离 更新j号点的距离
            {
                dist[j] = dist[ver] + w[i];
                heap.push({dist[j], j}); //更新后的距离存入堆中
            }
        }
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h); //初始化邻接表
    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
    }
    printf("%d\n", dijkstra());
    return 0;
}

//add函数内会存储重边和其对应的权重(距离),从而形成很多冗余的点 
//在【1】位置处 假如i号点有重边且i号点是当前堆内和源点距离最近的点(也就是位于堆顶)
//取出的时候会先弹出其距离最小值和编号 然后st[i]=true, 当堆内下次弹出i号点和他的重边时,由于st[i]=true 就会直接continue 跳出本次循环 可以节省运行时间
posted @ 2024-04-16 17:40  MsEEi  阅读(13)  评论(0)    收藏  举报