题目大意:给定一个无向图,图顶点数N≤5000,边数R≤100000,求顶点1到顶点N的“次短路”。
注意:
- 多次经过同一顶点或同一条边的路径是被允许的(允许有环的路径存在)。
- “次短路“指的是数值上的次短,例如,顶点1到N有两条最短路径,长度均为100,还有一条路径长为101,则次短路长度应输出101,而非100。
// Poj 3255 Roadblocks
// By Victor Li
// 2016-09-03
// SPFA算法解次短路问题
// SPFA算法不一定局限于求解最短路问题,图中的有状态表示和转移的最优化问题都可以考虑应用SPFA算法的变种来处理
// SPFA算法适用于稀疏图
#include <stdio.h>
#include <stdlib.h>
#include <queue>
using namespace std;
const int oo = 5*10e8;
struct enode{
int v;
int d;
enode * next;
};
void addEdge(int x, int y, int d, enode **e, enode **en){
if (en[x] == 0){
e[x] = new enode;
e[x]->v = y;
e[x]->d = d;
e[x]->next = NULL;
en[x] = e[x];
} else {
en[x]->next = new enode;
en[x]->next->v = y;
en[x]->next->d = d;
en[x]->next->next = NULL;
en[x] = en[x]->next;
}
}
//松弛操作
int relax(int x, int y, int d, int * dist){
if (dist[x] + d < dist[y]){
int tmp = dist[y];
dist[y] = dist[x] + d;
return tmp;
} else
return -1;
}
int main(){
int n, m, x, y, d, tmp;
scanf("%d%d",&n,&m);
//用链表存储每个顶点连接的所有边的信息;e[i]为顶点i对应的边链表的表头
//为了提高构建边链表的效率,用en[i]存储顶点i对应的边链表的末尾
enode ** e = (enode **) calloc(n, sizeof(enode *));
enode ** en = (enode **) calloc(n, sizeof(enode *));
for (int i=0;i<m;i++){
scanf("%d%d%d",&x,&y,&d); --x; --y;
addEdge(x,y,d,e,en);
addEdge(y,x,d,e,en);
}
//0 =< i < n : dist[i] 从顶点0到顶点i的最短路长度
//n =< i < 2*n : dist[i] 从顶点0到顶点i-n的次短路的长度
int * dist = (int *) calloc(2*n, sizeof(int ));
for (int i=1;i<2*n;i++) dist[i] = oo;
bool * flag = (bool *) calloc(2*n, sizeof(bool));
flag[0] = true;
//应用SPFA算法求解
queue<int> Q;
Q.push(0);
while (!Q.empty()){
x = Q.front();
Q.pop();
flag[x] = false;
if (x<n) {
enode * et = e[x];
while (et!=NULL){
//这里使用tmp记录下了“松弛”过程中被更短的路径顶掉的之前的最短路径的长度
//记录下它是因为它有可能是到达该顶点的“次短路”,可以用来更新dist[et->v+n]
if ( (tmp = relax(x,et->v,et->d,dist))>-1 ){
if (!flag[et->v]){
Q.push(et->v);
flag[et->v] = true;
}
if ( tmp<dist[et->v+n]){
dist[et->v+n] = tmp;
if (!flag[et->v+n]){
Q.push(et->v+n);
flag[et->v+n] = true;
}
}
} else {
//当前路径长度更新不了到达该节点的最短路径,则再尝试更新次短路径
if ( dist[x]+et->d>dist[et->v] && relax(x,et->v+n,et->d,dist)>-1 )
if (!flag[et->v+n]){
Q.push(et->v+n);
flag[et->v+n] = true;
}
}
et = et->next;
}
} else {
//x>=n,说明当前的队头对应的是一条“次短路径”
//次短路径不可能经过一条边到达下一个顶点时就成为到达下一顶点的最短路径
//因此这里只考虑更新下一顶点的次短路径的情况
enode * et = e[x-n];
while (et!=NULL){
if (relax(x,et->v+n,et->d,dist)>-1)
if (!flag[et->v+n]){
Q.push(et->v+n);
flag[et->v+n] = true;
}
et = et->next;
}
}
}
//该算法可以求出到达图中每一节点的最短路径和次短路径
printf("%d\n",dist[2*n-1]);
return 0;
}