AcWing 850. Dijkstra求最短路 II

题目传送门

一、解题思路

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

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

  2. (n)(结点数量)有明显区别:(I)(500)上限,(II)(1.5 * 10^5)。如果按(I)的方式去解,创建邻接矩阵,那个二维邻接矩阵就是 (1.5 * 10^5 * 1.5 * 10^5), 这样就太大了,内存爆掉,这就是使用邻接表更合适了。另外,可以采用小顶堆对朴素版本的(Dijkstra)进行一次优化。

二、C++ 代码

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;
const int INF = 0x3f3f3f3f;

typedef pair<int, int> PII;             // 维护距离和编号,所有是一个pair
int n, m;                               // 点的数量
int h[N], w[N], e[N], ne[N], idx;       // 邻接表,稀疏图
int dist[N];                            // 存储所有点到1号点的距离
bool st[N];                             // 存储每个点的最短距离是否已确定

void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra() {
    //距1号点距离初始化
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    //STL的优先队列(小根堆)
    priority_queue<PII, vector<PII>, greater<PII>> heap;//这是固定写法,背一下吧
    heap.push({0, 1});                  // first存储距离,second存储节点编号,1号点放进去

    //bfs
    while (heap.size()) {
        //每次用堆的特性,找到堆里最小的点,就是距离最小的点
        auto t = heap.top();
        heap.pop();//找到就弹出

        int k = t.second;           //编号
        int distance = t.first;     // 距离
        //如果已经处理过了,是一个冗余的备份(比如重边)
        if (!st[k]) {
            //标识已使用
            st[k] = true;
            //遍历每一条边
            for (int i = h[k]; i != -1; i = ne[i]) {
                int j = e[i];
                if (dist[j] > distance + w[i]) {
                    dist[j] = distance + w[i]; //松弛
                    heap.push({dist[j], j});    //把j点放到优先队列中去
                }
            }
        }
    }
    
    //如果松弛无效,表示无法到达
    if (dist[n] == INF) return -1;
    //返回最短距离
    return dist[n];
}

int main() {
    //读入优化
    ios::sync_with_stdio(false);
    cin >> n >> m;
    //表头初始化,链式前向星专用
    memset(h, -1, sizeof h);
    //读入m条边
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);//就算有重边,我们这里也不做处理,因为算法会保证如何最优
    }
    int t = dijkstra();
    printf("%d
", t);
    return 0;
}

三、问题集

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

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

  • (849)(850)的代码互通吗?
    因为(850)使用的是邻接表,而(849)使用的是邻接矩阵,所以,(850)的代码兼容性更强,支持更多的结点数,可以(AC)(849),但反过来就不行了,(849)的代码会因为开不了(1.5*10^5*1.5*10^5)而导致开不出来二维矩阵,无法运行。

原文地址:https://www.cnblogs.com/littlehb/p/15319543.html