题目地址:
https://www.acwing.com/problem/content/description/4199/
给定一个 n n n个点 m m m条边的的无向图。点的编号从 1 1 1到 n n n。图中可能包含重边和自环。请你找到并输出一条从点 1 1 1到点 n n n的最短路径。
输入格式:
第一行包含两个整数
n
,
m
n,m
n,m。接下来
m
m
m行,每行包含三个整数
a
,
b
,
w
a,b,w
a,b,w,表示点
a
a
a和点
b
b
b之间存在一条无向边,边长为
w
w
w。
输出格式:
如果最短路径不存在,则输出
−
1
−1
−1。否则,在一行内输出从点
1
1
1到点
n
n
n的最短路径中依次包含的点的编号,各点编号之间用空格隔开。如果答案不唯一,输出任意合理方案均可。
数据范围:
前六个测试点满足
2
≤
n
≤
10
2≤n≤10
2≤n≤10,
1
≤
m
≤
10
1≤m≤10
1≤m≤10。
所有测试点满足
2
≤
n
≤
1
0
5
2≤n≤10^5
2≤n≤105,
1
≤
m
≤
1
0
5
1≤m≤10^5
1≤m≤105,
1
≤
a
,
b
≤
n
1≤a,b≤n
1≤a,b≤n,
1
≤
w
≤
1
0
6
1≤w≤10^6
1≤w≤106。
本题不卡spfa算法。
直接用Dijkstra算法。如果存在解的话,则在Dijkstra树上从 1 1 1到 n n n的路径即为一个合法路径。可以用pre数组来记录每个点由谁更新而来,从而把路径记下来。最后输出路径的时候从 n n n倒着推回去即可。代码如下:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
using PLI = pair<long, int>;
const int N = 1e5 + 10, M = 2e5 + 10;
int n, m;
int h[N], e[M], ne[M], w[M], idx;
long dist[N];
int pre[N];
bool vis[N];
int res[N];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
void dijkstra() {
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
priority_queue<PLI, vector<PLI>, greater<>> heap;
heap.push({0, 1});
pre[1] = 1;
while (heap.size()) {
auto t = heap.top(); heap.pop();
int v = t.second;
if (vis[v]) continue;
// 到了n号点了,找到了到n的最短路,退出
if (v == n) break;
vis[v] = true;
for (int i = h[v]; ~i; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[v] + w[i]) {
dist[j] = dist[v] + w[i];
pre[j] = v;
heap.push({dist[j], j});
}
}
}
}
int main() {
memset(h, -1, sizeof h);
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
dijkstra();
if (dist[n] == 0x3f3f3f3f3f3f3f3f) puts("-1");
else {
int cnt = 0;
while(n != 1) {
res[cnt++] = n;
n = pre[n];
}
res[cnt++] = 1;
// 倒着输出,得出路径
for (int i = cnt - 1; i >= 0; i--) printf("%d ", res[i]);
}
}
时间复杂度 O ( m log n ) O(m\log n) O(mlogn),空间 O ( n ) O(n) O(n)。