题目地址:
https://www.acwing.com/problem/content/description/852/
给定一个 n n n个点 m m m条边的有向图,图中可能存在重边和自环,所有边权均为非负值。求 1 1 1号点到 n n n号点的最短距离,如果无法从 1 1 1号点走到 n n n号点,则输出 − 1 -1 −1。
输入格式:
第一行包含整数
n
n
n和
m
m
m。接下来
m
m
m行每行包含三个整数
x
x
x,
y
y
y,
z
z
z,表示存在一条从点
x
x
x到点
y
y
y的有向边,边长为
z
z
z。
输出格式:
输出一个整数,表示
1
1
1号点到
n
n
n号点的最短距离。如果路径不存在,则输出
−
1
-1
−1。
数据范围:
1
≤
n
,
m
≤
1.5
×
1
0
5
1\le n,m\le 1.5\times 10^5
1≤n,m≤1.5×105
0
≤
e
≤
10000
0\le e\le 10000
0≤e≤10000
e
e
e是边长。
由数据范围得知这个图是个稀疏图,可以用堆优化版的Dijkstra算法来做。具体思路可以参考https://blog.youkuaiyun.com/qq_46105170/article/details/113816110。堆优化,主要可以优化”选取当前未算出的点中最短路最近的那个点“这一步上,可以开个最小堆,每次都pop出最短路最近的那个点。但是由于在更新的时候,某些点的上次更新的最短路距离其实已经入堆了,但是直接更新堆里的元素非常麻烦,简单起见,可以让顶点重复入堆,只需在出堆的时候判断一下即可,即当发现出堆的元素最短路之前已经算出来过了,那就直接略过。否则的话,出堆的点的最短路就已经算出了,用它来更新其邻接点的最短路,如果其邻接点最短路被更新成更小的值了,则入堆。代码如下:
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
using PII = pair<int, int>;
const int N = 150010, INF = 0x3f3f3f3f;
int n, m;
int h[N], e[N], w[N], ne[N], idx;
int dist[N];
bool vis[N];
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], 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});
while (heap.size()) {
auto &[dis, v] = heap.top(); heap.pop();
if (vis[v]) continue;
if (v == n) break;
vis[v] = true;
for (int i = h[v]; ~i; i = ne[i]) {
int j = e[i];
if (!vis[j] && dist[j] > dis + w[i]) {
dist[j] = dis + w[i];
heap.push({dist[j], j});
}
}
}
return dist[n] == INF ? -1 : dist[n];
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m;
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
cout << dijkstra() << endl;
}
时间复杂度 O ( m log n ) O(m\log n) O(mlogn),空间 O ( n ) O(n) O(n)。