You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##AcWing 850. Dijkstra求最短路 II

一、题目描述

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

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

输入格式 第一行包含整数 nm

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

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

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

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

二、解题思路

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

  • 两者m(边的数量)的数据范围基本一致,I10^5,II1.5*10^5,是一个数量级的

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

三、实现代码

#include <bits/stdc++.h>

using namespace std;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N = 150010, M = N << 1;

int st[N];
int dis[N]; // 距离数组

// 邻接表
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(dis, 0x3f, sizeof dis);
    dis[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> q; // 小顶堆
    q.push({0, 1});

    while (q.size()) {
        PII t = q.top();
        q.pop();
        int u = t.second;
        if (!st[u]) {
            st[u] = 1;
            for (int i = h[u]; ~i; i = ne[i]) {
                int v = e[i];
                if (dis[v] > dis[u] + w[i]) {
                    dis[v] = dis[u] + w[i];
                    q.push({dis[v], v});
                }
            }
        }
    }
    if (dis[n] == INF) return -1;
    return dis[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:849850的代码互通吗? A:因为850使用的是邻接表,而849使用的是邻接矩阵,所以,850的代码兼容性更强,支持更多的结点数,可以AC849,但反过来就不行了,849的代码会因为开不了1.5*10^5*1.5*10^5而导致开不出来二维矩阵,无法运行。