洛谷题单指南-最短路-P2865 [USACO06NOV] Roadblocks G
原题链接:https://www.luogu.com.cn/problem/P2865
题意解读:求次短路长度。
解题思路:
方法一、求最短路的同时更新次短路
设d1[],d2[]分别保存节点的最短路、次短路,利用Dijikstra算法,每次从优先队列中取距离起点最短的路径d、节点u,
如果d>d2[u],说明此时路径比次短路大,必然无法用来更新次短路和最短路,忽略;
枚举u的所有邻接点,设为v,u-v边长w
如果d1[v]>d+w,则更新次短路d2[v]=d1[v],更新最短路d1[v]=d+w;
否则如果d1[v]<d+ w同时d2[v]>d+w,则更新次短路d2[v]=d+w。
100分代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 5005, M = 200005;
int h[N], e[M], w[M], ne[M], idx;
int d1[N], d2[N]; //d1距离起点最短路,d2距离起点次短路
int n, m;
void add(int a, int b, int c)
{
e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}
void dijikstra()
{
memset(d1, 0x3f, sizeof(d1));
memset(d2, 0x3f, sizeof(d2));
d1[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> pq;
pq.push({0, 1});
while(pq.size())
{
PII p = pq.top(); pq.pop();
int u = p.second, d = p.first;
if(d > d2[u]) continue; //剪枝,当前u的距离比次短路还大,说明u不可能用来更新相邻节点的次短路
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(d1[v] > d + w[i])
{
d2[v] = d1[v];
d1[v] = d + w[i];
pq.push({d1[v], v});
}
else if(d1[v] < d + w[i] && d2[v] > d + w[i])
{
d2[v] = d + w[i];
pq.push({d2[v], v});
}
}
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof(h));
for(int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
add(u, v, w), add(v, u, w);
}
dijikstra();
cout << d2[n];
return 0;
}
如果对以上代码中if(d > d2[u]) continue;不好理解,可以进一步细化:
将最短路、次短路的更新和标记独立开来,只要秉持一个原则:一个节点的最短路或次短路只能由最短路或者次短路来更新。
每次加入优先队列的节点需要区分是计算过最短路还是次短路,这样就可以用vis1、vis2分别来进行标记。
可以得到以下代码:
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 5005, M = 200005;
struct Node
{
int id; //节点编号
int type; //1:最短路 2:次短路
int dist; //距离起点的最短路或者次短路
bool operator < (const Node & x) const
{
return dist > x.dist;
}
};
int h[N], e[M], w[M], ne[M], idx;
int d1[N], d2[N]; //d1距离起点最短路,d2距离终点次最短路
bool vis1[N], vis2[N]; //vis1最短路标记,vis2次短路标记
int n, m;
void add(int a, int b, int c)
{
e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}
void dijikstra()
{
memset(d1, 0x3f, sizeof(d1));
memset(d2, 0x3f, sizeof(d2));
d1[1] = 0;
priority_queue<Node> pq;
pq.push({1, 1, 0});
while(pq.size())
{
Node node = pq.top(); pq.pop();
int u = node.id, type = node.type, dist = node.dist;
if(type == 1 && vis1[u] || type == 2 && vis2[u]) continue;
if(type == 1) vis1[u] = true;
else vis2[u] = true;
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(d1[v] > dist + w[i]) //如果是更优的最短路
{
d2[v] = d1[v]; //最短路变成次短路
pq.push({v, 2, d2[v]});
d1[v] = dist + w[i]; //更新最短路
pq.push({v, 1, d1[v]});
}
else if(d1[v] < dist + w[i] && d2[v] > dist + w[i]) //如果是更优的次短路
{
d2[v] = dist + w[i]; //更新次短路
pq.push({v, 2, d2[v]});
}
}
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof(h));
for(int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
add(u, v, w), add(v, u, w);
}
dijikstra();
cout << d2[n];
return 0;
}
方法二、从起点、终点分别跑一遍Dijikstra,再枚举
设d1[],d2[]分别表示距离起点的最短路、距离终点的最短路,
从起点、终点分别跑Dijikstra更新d1、d2,
枚举所有的边:u->v,长度w
求不等于起点到终点最短路的d1[u] + d2[v] + w的最小值,即是次短路。
100分代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 5005, M = 200005;
int h[N], e[M], w[M], ne[M], idx;
int d1[N], d2[N]; //d1距离起点最短路,d2距离终点次最短路
bool vis[N];
int n, m;
void add(int a, int b, int c)
{
e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}
void dijikstra(int s, int d[N])
{
memset(d, 0x3f, sizeof(d1));
memset(vis, false, sizeof(vis));
d[s] = 0;
priority_queue<PII, vector<PII>, greater<PII>> pq;
pq.push({0, s});
while(pq.size())
{
PII p = pq.top(); pq.pop();
int u = p.second;
if(vis[u]) continue;
vis[u] = true;
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(d[v] > d[u] + w[i])
{
d[v] = d[u] + w[i];
pq.push({d[v], v});
}
}
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof(h));
for(int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
add(u, v, w), add(v, u, w);
}
dijikstra(1, d1);
dijikstra(n, d2);
int ans = INT_MAX;
for(int u = 1; u <= n; u++)
{
for(int j = h[u]; ~j; j = ne[j])
{
int v = e[j];
int len = d1[u] + d2[v] + w[j];
if(len != d1[n]) ans = min(ans, len);
}
}
cout << ans;
return 0;
}
浙公网安备 33010602011771号