AcWing 850. Dijkstra求最短路 II

\(AcWing\) \(850\). \(Dijkstra\)求最短路 \(II\)

一、题目描述

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

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

输入格式
第一行包含整数 \(n\)\(m\)

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

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

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

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

二、解题思路

此题与 \(Dijkstra\)求最短路\(I\) 有什么本质上的区别?

  • 两者\(m\)(边的数量)的数据范围基本一致,\(I\)\(10^5\),\(II\)\(1.5*10^5\),是一个数量级的

  • \(n\)(结点数量)有明显区别:\(I\)\(500\)上限,\(II\)\(1.5 * 10^5\)。可以采用小顶堆对朴素版本的\(Dijkstra\)进行一次优化。

三、实现代码

#include <bits/stdc++.h>

using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1.5 * 1e5 + 10, M = N;
int st[N];
int d[N]; // 距离数组用d,二元组中第一维记为dist,以视区分
typedef pair<int, int> PII;
// 邻接表
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int n, m;
int dijkstra() {
    memset(d, 0x3f, sizeof d);
    d[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> q;
    q.push({0, 1});
    while (q.size()) {
        auto t = q.top();
        q.pop();
        int dist = t.first;
        int u = t.second;
        if (!st[u]) {
            st[u] = 1;
            for (int i = h[u]; ~i; i = ne[i]) {
                int v = e[i];
                if (d[v] > dist + w[i]) {
                    d[v] = dist + w[i];
                    q.push({d[v], v});
                }
            }
        }
    }
    if (d[n] == INF) return -1; // 单源最短路
    return d[n];
}
int main() {
    cin >> n >> m;
    memset(h, -1, sizeof h);
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    printf("%d\n", dijkstra());
    return 0;
}

四、问题集

  • \(Q1\):优先队列的数据类型应该是怎样的呢?
    \(A\):我们知道优先队列应该用于快速寻找距离最近的点。由于优先队列只是将最小的那个元素排在前面,因此我们应该定义一种数据类型,使得它包含该节点的编号以及该节点当前与起点的距离。

  • \(Q2:\)如果一个结点到起点的最短距离可以随多个中间点的加入而变化,那不是加入队列多次吗,如何处理队列中的那个已经存入的元素?
    \(A\):事实上,不需要理会队列中的元素,而是再存入一个就行了。因为如果要发生变化,只能将节点与起点之间的距离变得更小,而优先队列恰好是先让最小的那个弹出。
    因此,轮到某一个队列元素弹出的时候,如果有多个元素的节点编号相同,那么被弹出的一定是节点编号最小的一个。等到后面再遇到这个节点编号的时候,我们只需要将它忽略掉就行了。

  • \(Q3\):\(849\)\(850\)的代码互通吗?
    \(A\):因为\(850\)使用的是邻接表,而\(849\)使用的是邻接矩阵,所以,\(850\)的代码兼容性更强,支持更多的结点数,可以\(AC\)\(849\),但反过来就不行了,\(849\)的代码会因为开不了\(1.5*10^5*1.5*10^5\)而导致开不出来二维矩阵,无法运行。
posted @ 2021-09-22 13:35  糖豆爸爸  阅读(566)  评论(0编辑  收藏  举报
Live2D