题意:求 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 ", d2[n]); return 0; }
,并将最短边赋给次短边;或者如果大于最短变且小于次短边,则赋给次短边。两次完成之后均要加入队列