这是一道次短路模板题。作为一名 SPFA 信仰选手我决定用 SPFA 来解决这道题。
首先呢,思路还算简单,整个程序就是分别从节点 1 和 n 跑一遍最短路,然后进行判断。
如何判断,是本题的重点。
我们可以枚举每一个点分别到 1 和 n 的最短路之和,对所有点求一个次小值(最小值就是最短路),就是次短路,即:
d i s 1 [ n ] < d i s 1 [ i ] + d i s 2 [ i ] dis1[n]<dis1[i]+dis2[i] dis1[n]<dis1[i]+dis2[i]
但这样只能得50分,因为它有一个漏洞,那就是自环。对于自环,上式就相当于取自环中最短的一条边进行判断,如果不满足条件则放弃这个点。但如果这条最短的边恰巧是最短路,而自环中又恰巧有比它稍微大一点点的边,成为次短路的一部分,那么,它就被忽略了。
所以,要按每一条边进行枚举。
怎么枚举呢?要知道,判断最短路时是通过松弛:
d i s [ v ] > m a p [ k ] . w + d i s [ u ] dis[v] > map[k].w+dis[u] dis[v]>map[k].w+dis[u]
来判断,那么,次短路亦可以用类似的办法,即:
d i s 1 [ n ] < d i s 1 [ i ] + d i s 2 [ v ] + m a p [ k ] . w dis1[n]<dis1[i]+dis2[v]+map[k].w dis1[n]<dis1[i]+dis2[v]+map[k].w
对于每个点 i,枚举与它相连的所有点之间的所有边,判断这个值与最短路的关系,这样就不会有遗漏了。
这样跑下来时间复杂度应该是 O(nm) 的,看一眼数据范围,哎嘛舒服,直接过了。
C++:
#include <stdio.h>
#include <queue>
#include <iostream>
#include <string.h>
using namespace std;
#define MAXN 100005
int m,n,cnt;
int dis1[MAXN],dis2[MAXN],vis[MAXN],head[MAXN*2];
queue <int>que;
struct node{
int to,next,w;
}map[MAXN*2];
void add(int u,int v,int w){
map[++cnt] = (node){v,head[u],w};
head[u] = cnt;
}
void SPFA(int u,int *dis){
memset(vis,0,sizeof(vis));
que.push(u);
dis[u] = 0;
vis[u] = 1;
while(!que.empty()){
int u = que.front(); que.pop();
vis[u] = 0;
for(int k=head[u];k;k=map[k].next){
int v = map[k].to;
int w = map[k].w+dis[u];
if( dis[v] > w){
dis[v] = w;
if(!vis[v]){
vis[v] = 1;
que.push(v);
}
}
}
}
}
int main(void){
memset(dis1,0x3f,sizeof(dis1));
memset(dis2,0x3f,sizeof(dis2));
cin >> n >> m;
for(int i=1;i<=m;i++){
int u,v,w;
cin >> u >> v >> w;
add(u,v,w); add(v,u,w);
}
SPFA(1,dis1); SPFA(n,dis2);
int ans=0x3f3f3f3f;
for(int i=1;i<=n;i++){
for(int k=head[i];k;k=map[k].next){
int v = map[k].to;
int w = dis1[i]+dis2[v]+map[k].w;
if(dis1[n]<w && ans>w) ans = w;
}
}
cout << ans << endl;
return 0;
}