背景:
好久之前的题目了
.
.
.
...
...
题目传送门:
https://www.luogu.org/problemnew/show/P2865
题意:
求严格次短路。
思路:
本来是一道
s
p
f
a
spfa
spfa傻逼题。
仔细一想发现好像之前的思路有问题,那就来细讲一下吧。
d
i
s
i
,
0
dis_{i,0}
disi,0表示最短路,
d
i
s
i
,
1
dis_{i,1}
disi,1表示次短路。
考虑当前能否更新最短路。若可以则更新当前次短路和最短路;否则,考虑更新次短路。
如何更新次短路,我原来一直以为用上一次的最短路更新即可,但只有
50
p
t
s
50pts
50pts,细想一下发现要还用上次的次短路更新。
见图:
如果我们假设最短路是
A
−
>
B
−
>
C
−
>
E
−
>
F
A->B->C->E->F
A−>B−>C−>E−>F且
3
⋅
(
∣
B
C
⃗
∣
+
∣
C
E
⃗
∣
)
<
∣
B
D
⃗
∣
+
∣
D
E
⃗
∣
3\cdot(|\vec{BC}|+|\vec{CE}|)<|\vec {BD}|+|\vec{DE}|
3⋅(∣BC∣+∣CE∣)<∣BD∣+∣DE∣,那么次短路就是
A
−
>
B
−
>
D
−
>
E
−
>
F
A->B->D->E->F
A−>B−>D−>E−>F。
假设且一开始更新了
A
−
>
B
−
>
C
−
>
E
−
>
F
A->B->C->E->F
A−>B−>C−>E−>F,那么对于
E
E
E点而言,我们的次短路就不能通过
C
C
C或
D
D
D点的最短路来更新,而是用
D
D
D点的次短路更新。
改了一下之前的代码~~(原来的太丑)~~ 。
代码:
#include<cstdio>
#include<cstring>
#include<queue>
#define LL long long
using namespace std;
queue <int> f;
struct node{int x,y,next;LL z;} a[300010];
bool bz[10010];
int last[10010];
LL dis1[10010],dis2[10010];
int n,m,len=0;
void ins(int x,int y,LL z)
{
a[++len]=(node){x,y,last[x],z}; last[x]=len;
}
void PUSH(int x)
{
if(bz[x]) bz[x]=false,f.push(x);
}
void spfa()
{
memset(bz,true,sizeof(bz));
memset(dis1,63,sizeof(dis1));
memset(dis2,63,sizeof(dis2));
dis1[1]=0;
bz[1]=false;
f.push(1);
while(!f.empty())
{
int x=f.front();
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if(dis1[x]+a[i].z<dis1[y])
{
if(dis2[y]>dis1[y]) dis2[y]=dis1[y];
dis1[y]=dis1[x]+a[i].z;
PUSH(y);
}
if(dis1[x]+a[i].z>dis1[y]&&dis1[x]+a[i].z<dis2[y]) dis2[y]=dis1[x]+a[i].z,PUSH(y);
if(dis2[x]+a[i].z<dis2[y]) dis2[y]=dis2[x]+a[i].z,PUSH(y);
}
bz[x]=true;
f.pop();
}
}
int main()
{
int x,y;
LL z;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d %d %lld",&x,&y,&z);
ins(x,y,z),ins(y,x,z);
}
spfa();
printf("%lld",dis2[n]<(1ll<<60)?dis2[n]:-1);
}