题解 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]);
}
posted @ 2021-08-11 10:29  recollector  阅读(61)  评论(0编辑  收藏  举报