题目链接https://www.luogu.org/problemnew/show/P1491
就是求第一个点到第n个点的次短路
第一次spfa用前驱记录最短路
第二次spfa删去最短路中的一条边
记录前驱开一个pre数组,在每次松弛操作时,如果d[i]被更新就pre[i]=j;表示当前到第i个点的最短路中,j是i的前驱节点
主函数中,从第n个点开始,即i=n,不断地进行找前驱i=pre[i] 直到pre[i]=1,这个过程中的点连成路径
为什么要去掉最短路上的一条边呢?
因为次短路和最短路至少有一条边不是共有的,需要想想理解一下,,,
code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=208;
int n,m;
struct p{
int x,y;
}po[maxn];
struct re{
int t;
double dis;
};
vector<re>g[maxn];
int pre[maxn];
double distance(int x1,int y1,int x2,int y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void spfa1()
{
queue<int>q;
double d[maxn];
bool vis[maxn];
memset(vis,0,sizeof(vis));
for(int i=2;i<=n;i++) d[i]=99999999;
d[1]=0;vis[1]=1;
q.push(1);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=0;i<g[u].size();i++)
{
int t=g[u][i].t;
double w=g[u][i].dis;
if(d[t]>d[u]+w)
{
d[t]=d[u]+w;
pre[t]=u;
if(!vis[t]){
q.push(t);
vis[t]=1;
}
}
}
}
}
double spfa2(int x,int y)
{
queue<int>q;
bool vis[maxn];
double d[maxn];
memset(vis,0,sizeof(vis));
for(int i=2;i<=n;i++) d[i]=99999999;
d[1]=0;vis[1]=1;
q.push(1);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=0;i<g[u].size();i++)
{
int t=g[u][i].t;
double w=g[u][i].dis;
if((u==x&&t==y)||(u==y&&t==x)) continue;
if(d[t]>d[u]+w)
{
d[t]=d[u]+w;
if(!vis[t]){
q.push(t);
vis[t]=1;
}
}
}
}
return d[n];
}
int main()
{
int pj,qj;
double dis;
double ans=99999999;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d%d",&po[i].x,&po[i].y);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&pj,&qj);
dis=distance(po[pj].x,po[pj].y,po[qj].x,po[qj].y);
g[pj].push_back((re){qj,dis});
g[qj].push_back((re){pj,dis});
}
memset(pre,0,sizeof(pre));
spfa1();
int now=n;
while(pre[now]!=1){
double sxy=spfa2(now,pre[now]);
if(ans>sxy) ans=sxy;
now=pre[now];
}
double sxy=spfa2(now,1);
if(ans>sxy) ans=sxy;
if(ans==99999999) printf("-1");
else printf("%.2f",ans);
return 0;
}