题解 P2865 [USACO06NOV]Roadblocks G
给定一张 \(n\) 个点 \(m\) 条边的无向图,求 \(1\) 到 \(n\) 的严格次短路。
\(1 \leq n \leq 5\times 10^3 ,1 \leq m \leq 10^5\) 。
记 \(1\) 到 \(x\) 的最短路为 \(d_x\) ,次短路为 \(cd_x\) 。
考虑 Dijkstra ,通过归纳法很容易证明一个点第二次出队就能得到次短路。
在考虑通过一条边 \((u,v,w)\) ,用 \(u\) 的最短路更新 \(v\) 的最短路或次短路的时候,有两种情况。
-
\(d_v>d_u+w\) ,那么原本 \(v\) 的次短路应更新为原 \(v\) 的最短路,最短路更新为 \(d_u+w\) ,即:
\[cd_v\gets d_v,d_v\gets d_u+w \] -
\(d_v<d_u+w\) 但是 \(cd_v>d_u+w\) ,直接更新即可。
通过次短路更新 \(v\) 的最短路和次短路同理。
这里有个技巧:每次松弛的时候不需要更新四次,直接用这次出队时取出的最短路(次短路)这一个值更新即可。因为另一个要不还不是最短的,要不已经更新过了,多次更新没有意义。
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cctype>
#include <queue>
using namespace std;
inline int read() {
int num = 0 ,f = 1; char c = getchar();
while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
return num * f;
}
const int N = 5e3 + 5 ,M = 2e5 + 5 ,INF = 0x3f3f3f3f;
struct Edge {
int to ,w ,next;
Edge (int to = 0 ,int w = 0 ,int next = 0) : to(to) ,w(w) ,next(next) {}
}G[M]; int head[N] ,idx;
inline void add(int u ,int v ,int w) {
G[++idx] = Edge(v ,w ,head[u]); head[u] = idx;
G[++idx] = Edge(u ,w ,head[v]); head[v] = idx;
}
struct node {
int id ,dis;
node (int id = 0 ,int dis = 0) : id(id) ,dis(dis) {}
friend bool operator < (const node &a ,const node &b) {
return a.dis > b.dis;
}
};
int dis[N] ,cdis[N] ,done[N] ,n;
inline void dijkstra(int n ,int s) {
for (int i = 1; i <= n; i++)
dis[i] = cdis[i] = INF ,done[i] = 0;
priority_queue <node> q;
dis[s] = 0; q.push(node(s ,0));
while (!q.empty()) {
int now = q.top().id ,dist = q.top().dis; q.pop();
if (done[now] > 1) continue;
done[now]++;
for (int i = head[now]; i ; i = G[i].next) {
int v = G[i].to ,w = G[i].w;
if (done[v] > 1) continue;
if (dis[v] > dist + w) {
cdis[v] = dis[v] ,dis[v] = dist + w;
q.push(node(v ,dis[v]));
}
else if (cdis[v] > dist + w) {
cdis[v] = dist + w;
q.push(node(v ,cdis[v]));
}
}
}
}
int m;
signed main() {
n = read() ,m = read();
for (int i = 1; i <= m; i++) {
int u = read() ,v = read() ,w = read();
add(u ,v ,w);
}
dijkstra(n ,1);
printf("%d\n" ,cdis[n]);
}