题意
这题给你一个无向图,让你求出次短路(比最短路长,比其他路短).
思维
这里要注意到题目是人类出的,也是给人类做的,更何况这只是一道蓝题,所以极大概率不是让你发明新算法.因此这里最有可能是最短路的变形题.我们发现次短路与最短路是有关系的,因为构成一个次短路只有两种可能:
1.固定一条边,求出起点和终点分别到端点的最短路径,这种路径可能是次短路.注意判定是否你直接找到最短路了.
2.最短路的一条边被重复走了.为什么不是多条边被重复走?因为这样就比只重复一条边的长了,不是次短路.
两种枚举即可.
算法
这里求最短路dijikstra SPFA都可以,作者用的是堆优化的dijikstra,因为某个算法已经死了,如果这是考场且没有负权边一定用dijikstra.
实现
dis数组开两个(或者开一个二维的),分别存从起点和终点跑的最短路,这样思路中的两种枚举方式都可以简单实现.要注意,第一种方法中枚举不要出现走重的情况,因为这种情况绝对不是次短路.
cpp
#include <bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int N=5e3+10, M=1e5+10;
ll n,m,dis[N][2],mi,ans=(1e12),sl=(1e12);
bool f[N];
struct node{
ll u,d;
bool operator < (const node &u) const{
return u.d<d;
}
};
vector<node>v[N];
void dij(int s, int x){
priority_queue<node>q;
for(int i=1;i<=n;i++) dis[i][x]=(1e12), f[i]=0;
dis[s][x]=0, q.push({s,0});
while(q.size()){
node t=q.top();
q.pop();
if(f[t.u]) continue;
else f[t.u]=1;
for(int i=0;i<v[t.u].size();i++){
int u=v[t.u][i].u, g=v[t.u][i].d;
if(dis[u][x]>dis[t.u][x]+g){
dis[u][x]=dis[t.u][x]+g, q.push({u,dis[u][x]});
}
}
}
}
struct Edge{
int u,v,w;
}t[M];
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>t[i].u>>t[i].v>>t[i].w;
v[t[i].u].pb({t[i].v,t[i].w});
v[t[i].v].pb({t[i].u,t[i].w});
}
dij(1,0), dij(n,1), mi=dis[n][0];
for(int i=1;i<=m;i++){
ll u=t[i].u, v=t[i].v, w=t[i].w;
if(dis[u][0]+dis[v][1]>dis[u][1]+dis[v][0]) swap(u,v);
ll s=dis[u][0]+dis[v][1]+w;
if(s==mi) continue;
ans=min(ans,s);
}
for(int i=1;i<=m;i++){
ll u=t[i].u, v=t[i].v, w=t[i].w;
if(dis[u][0]+dis[v][1]>dis[u][1]+dis[v][0]) swap(u,v);
ll s=dis[u][0]+dis[v][1]+w;
if(s!=mi) continue;
sl=min(sl,w);
}ans=min(ans,mi+sl*2);
cout<<ans;
return 0;
}
这里两个swap可能匪夷所思,想象一根木棒两端固定在两个端点(起点和终点),很显然有两种摆法,一种摆法非常奇怪,绳子会长很多,因为这个摆法是从一条直线转了180度.这里就是如此.其他大家尽量理解.