傳送門
——在LYC大佬的幫助下過了這道題
思路:
LYC大佬的博客里已經講得很清晰了,我只是提一下要點。
求次短路,主要考慮兩個方面:
①在不重復走一條路的前提下,把最短路的其中一段替換為另一段。
②找出最短路中的最短的一條邊,重復走兩次。(走過來又走回去)
分別求出這兩方面所能算出的次短路的值,取小的那一條就是答案。
補充:
①讀入的邊要開結構體存起來,后面枚舉求次短路要使用。
②所謂枚舉,就是找出從開始一條一條的讀出邊的兩端點、權值。確定其是否在最短路上(即它的 左端點到源點的最短路+右端點到源點的最短路+自身的邊權 是否等於最短路長度。(注意細節:要判斷這條邊的兩端點到源點的最短路是否有重合部分,如下圖。)
設d[ a ]為 a 到源點的最短路,d[ b ]為 b 到源點的最短路,很顯然,兩條最短路有重合的地方,就需要將 a 點與 b 點交換位置,使得兩條最短路沒有重合,才能將 a->b 的權值加入。
代碼實現:
if(d1[x]+d2[y]>d1[y]+d2[x]) swap(x,y);
效果如下:
完整代碼:
#include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<string> #include<iostream> #include<algorithm> #include<queue> #include<stack> #include<deque> #include<set> #include<map> #include<vector> #include<fstream>
using namespace std; #define maxn 1000007
struct edge { int x,y,w; }dd[maxn]; struct hh { int nex,to,dis; }t[maxn]; priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q1; priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q2;//正反兩次最短路,兩個小根堆
int n,m,cnt=0,ans,mi; int vis[maxn],d1[maxn],d2[maxn],head[maxn]; inline int read() { char kr=0; char ls; for(;ls>'9'||ls<'0';kr=ls,ls=getchar()); int xs=0; for(;ls>='0'&&ls<='9';ls=getchar()) { xs=xs*10+ls-48; } if(kr=='-') xs=0-xs; return xs; } inline void add(int nex,int to,int w) { t[++cnt].nex=head[nex]; t[cnt].to=to; t[cnt].dis=w; head[nex]=cnt; } inline void dijkstra_first(int ww) { memset(d1,0x3f3f3f3f,sizeof(d1)); memset(vis,0,sizeof(vis)); q1.push(make_pair(0,ww)); d1[ww]=0; while(!q1.empty()) { int u=q1.top().second; q1.pop(); if(vis[u]) continue; vis[u]=1; for(int v=head[u];v;v=t[v].nex) { if(d1[t[v].to]>d1[u]+t[v].dis&&!vis[t[v].to]) { d1[t[v].to]=d1[u]+t[v].dis; q1.push(make_pair(d1[t[v].to],t[v].to)); } } } } inline void dijkstra_second(int ww) { memset(d2,0x3f3f3f3f,sizeof(d2)); memset(vis,0,sizeof(vis)); q2.push(make_pair(0,ww)); d2[ww]=0; while(!q2.empty()) { int u=q2.top().second; q2.pop(); if(vis[u]) continue; vis[u]=1; for(int v=head[u];v;v=t[v].nex) { if(d2[t[v].to]>d2[u]+t[v].dis&&!vis[t[v].to]) { d2[t[v].to]=d2[u]+t[v].dis; q2.push(make_pair(d2[t[v].to],t[v].to)); } } } }//兩次Dijkstra求正反最短路
int main() { n=read();m=read(); ans=999999; mi=999999; for(int i=1;i<=m;++i) { dd[i].x=read();dd[i].y=read();dd[i].w=read(); add(dd[i].x,dd[i].y,dd[i].w); add(dd[i].y,dd[i].x,dd[i].w); } dijkstra_first(1); dijkstra_second(n); int minn=d1[n]; for(int i=1;i<=m;i++) { int x=dd[i].x,y=dd[i].y; if(d1[x]+d2[y]>d1[y]+d2[x]) swap(x,y); int s=d1[x]+d2[y]; if(s+dd[i].w==minn) continue; ans=min(ans,s+dd[i].w); }//第一點:不重走邊
for(int i=1;i<=m;i++) { int x=dd[i].x,y=dd[i].y; if(d1[x]+d2[y]>d1[y]+d2[x]) swap(x,y); if(d1[x]+d2[y]+dd[i].w!=minn) continue; mi=min(mi,dd[i].w);//找出最短路中最短的邊
}//第二點:重走邊
ans=min(ans,minn+mi*2);//取較小值
printf("%d\n",ans); return 0; }
其實兩遍的Dijkstra函數可以簡化為一遍,多打一遍練練模板。