题意:求 1 - n 的次最短路
分析:
先来谈谈Dijkstra的优化。对于每次寻找到当前为访问过的点中距离最短的那一个,运用优先队列进行优化,避免全部扫描,每更新一个点的最短距离就加入优先队列。有人会问,一个点如果已经处理完成了,那它还留在队列中怎么办?我们放入队列时将一个点那时的顶点编号和最短距离进行打包,如果取出该点时,它当前的最短距离小于该点标记的最短距离,说明该点已经取到最短距离,不进行操作。或者直接用一个vis数组来记录某一个点是否已经取到最短距离;其次的优化是用邻接表存储与每一个点相连的所有边,方便处理。
这道题的做法和最短路径基本一致,唯一的不同点在于,在求出最短路径的情况下必须要保留下次短路径。对于Dijkstra判断中取出的每一个点,如果到它的最短距离大于当前该点的次短距离,则当前该点已经取到最短距离和次短距离,不进行操作,否则进行两次判断:如果小于最短边,则赋给最短边
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <stack>
#include <stdio.h>
#include <cmath>
#include <string.h>
#include <vector>
#define ll long long
using namespace std;
const int inf = 0x3f3f3f3f;
ll d[5050], d2[5050];
struct edge{
int u, v, w, next;
}e[200100];
int head[5050], visit[5050], cnt, n, r;
struct node
{
int id;
ll dist;
node(int _id = 0, ll _dist = 0) : id(_id), dist(_dist){}
bool operator < (const node & x) const
{
return dist > x.dist;
}
};
void add(int u, int v, int w)
{
e[cnt].u = u;
e[cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt++;
}
void dij()
{
for(int i = 0; i <= n; i++)
d[i] = d2[i] = inf;
priority_queue<node> q;
while(!q.empty())
q.pop();
d[1] = 0;
q.push(node(1, 0));
node p;
while(!q.empty())
{
p = q.top();
q.pop();
int u = p.id;
ll dis = p.dist;
if(d2[u] < dis)
continue;
for(int i = head[u]; i != -1; i = e[i].next)
{
int v = e[i].v;
ll dd = dis + e[i].w;
if(d[v] > dd)
{
swap(d[v], dd);
q.push(node(v, d[v]));
}
if(d2[v] > dd && dd > d[v])
{
d2[v] = dd;
q.push(node(v, d2[v]));
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
int u, v, w;
cnt = 0;
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &r);
for(int i = 0; i < r; i++)
{
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
dij();
printf("%lld\n", d2[n]);
return 0;
}
,并将最短边赋给次短边;或者如果大于最短变且小于次短边,则赋给次短边。两次完成之后均要加入队列