typora-copy-images-to: 图片
问题:
某街区共有R条道路、N个路口。道路可以双向通行。问1号路口到N号路口的次短路的长度是多少?次短路指的是比最短路长度长的次短路径。同一条边可以经过多次。
限制条件
1⩽N⩽50001 \leqslant N \leqslant 50001⩽N⩽5000
1⩽R⩽1000001 \leqslant R \leqslant 1000001⩽R⩽100000
输入示例:
N=4
R=4
1 2 100 //1 2表示节点,100表示1、2之间距离
2 4 200
2 3 250
3 4 100
输出示例:
450
思考:
求最短路径我们有Bellman-Ford、Dijkstra、Floyd算法。但是本题是求次最短路径。Dijkstra算法的思路是依次去确定尚未确定的顶点中距离最小的顶点。Dijkstra算法不断更新每个节点的最短路径,如果能够多加入有个缓存,存储上一次的最短距离,比如,设置d[MAX_V]、oldD[MAX_V]两个存储最短距离,每次更新d数组时同样更新oldD[MAX_V]保存次最短路径。
首先来看看Dijkstra算法:
#include <iostream>
#include <vector>
#include <queue>
#define MAX_V 1000
#define INF 100000000000
using namespace std;
struct edge {
int to, cost;
};
typedef pair<int, int> P;//first中存放距离,second中存放编号
int V;
vector<edge> G[MAX_V];
int d[MAX_V];
void dijkstra(int s) {
//优先级队列,从小到大排列
priority_queue<P, vector<P>, greater<P>> que;
fill(d, d + V, INF);
d[s] = 0;
que.push(P(0, s));
while (!que.empty()) {
P p = que.top();
que.pop();
int v = p.second;
if (d[v] < p.first) continue;//在这种情况下说明此记录已经作废,例如第一次循环插入了P(3,4);第二次循环又插入了P(2,4),显然,第二次的数据应该
//替换掉第一次数据,但是由于我们在这里使用了哦=优先级队列存储,如果要替换掉上一次的记录=会比较麻烦,所以我们不去除无效的数据,
//当从队列中取出数据时再判断是否是无效的数据(在之前应当被替换的数据)。
for (int i = 0; i < G[v].size(); i++) {
edge e = G[v][i];
if (d[e.to] > d[v] + e.cost) {
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to], e.to));
}
}
}
}
之前关于最短路径的介绍:
https://blog.youkuaiyun.com/qq_28120673/article/details/81224205
修改Dijkstra算法,加入oldD[MAX_V];
算法如下:
#include <iostream>
#include <vector>
#include <queue>
#define MAX_V 1000
#define INF 9999999
using namespace std;
struct edge {
int to, cost;
};
typedef pair<int, int> P;//first中存放距离,second中存放编号
int V;
vector<edge> G[MAX_V]; //图用邻接表表示
int d[MAX_V];
int oldD[MAX_V];
int N,R; //N个路口,R条道路
void init(){
cin>>N>>R;
V=N;
for(int i=0;i<V;i++){
int a,b,c;
cin>>a>>b>>c;
edge* e=new edge;
e->to=b-1;
e->cost=c;
G[a-1].push_back(*e);
edge* e1=new edge;
e1->to=a-1;
e1->cost=c;
G[b-1].push_back(*e1);
}
}
void dijkstra(int s) {
//优先级队列,从小到大排列
priority_queue<P, vector<P>, greater<P>> que;
fill(d, d + V, INF);
fill(oldD,oldD+V,INF);
d[s] = 0;
que.push(P(0, s));
while (!que.empty()) {
P p = que.top();
que.pop();
int v = p.second;
if (d[v] < p.first) continue;//在这种情况下说明此记录已经作废,例如第一次循环插入了P(3,4);第二次循环又插入了P(2,4),显然,第二次的数据应该
//替换掉第一次数据,但是由于我们在这里使用了优先级队列存储,如果要替换掉上一次的记录=会比较麻烦,所以我们不去除无效的数据,
//当从队列中取出数据时再判断是否是无效的数据(在之前应当被替换的数据)。
for (int i = 0; i < G[v].size(); i++) {
edge e = G[v][i];
int d2=d[v]+e.cost;
if (d[e.to] > d2) {
swap(d[e.to],d2);
que.push(P(d[e.to], e.to));
}
if(oldD[e.to]>d2 && d[e.to]<d2){ //上一个if执行过后,d[e.to]一定不会大于d2,所以这里排除,d[e.to]==d2的情况。
oldD[e.to]=d2;
que.push(P(oldD[e.to],e.to));
}
}
}
}
int main(){
init();
dijkstra(0);
for(int i=0;i<V;i++){
cout<<oldD[i]<<" ";
}
cout<<endl;
for(int i=0;i<V;i++){
cout<<d[i]<<" ";
}
return 0;
}
/* 注意这里的节点是从1开始编号,而算法中是从0开始编号
4 4
1 2 100
2 4 200
2 3 250
3 4 100
*/