题目
描述 Description
贝茜把家搬到了一个小农场,但她常常回到FJ的农场去拜访她的朋友。贝茜很喜欢路边的风景,不想那么快地结束她的旅途,于是她每次回农场,都会选择第二短的路径,而不象我们所习惯的那样,选择最短路。
贝茜所在的乡村有R(1<=R<=100,000)条双向道路,每条路都联结了所有的N(1<=N<=5000)个农场中的某两个。贝茜居住在农场1,她的朋友们居住在农场N(即贝茜每次旅行的目的地)。
贝茜选择的第二短的路径中,可以包含任何一条在最短路中出现的道路,并且,一条路可以重复走多次。当然咯,第二短路的长度必须严格大于最短路(可能有多条)的长度,但它的长度必须不大于所有除最短路外的路径的长度。
输入格式 Input Format
* 第1行: 两个整数,N和R,用空格隔开
- 第2…R+1行: 每行包含三个用空格隔开的整数A、B和D,表示存在一条长度为D(1 <= D <= 5000)的路连接农场A和农场B
输出格式 Output Format
输出一个整数,即从农场1到农场N的第二短路的长度
样例输入 Sample Input
4 4
1 2 100
2 4 200
2 3 250
3 4 100
样例输出 Sample Output
450
输出说明:
最短路:1 -> 2 -> 4 (长度为100+200=300)
第二短路:1 -> 2 -> 3 -> 4 (长度为100+250+100=450)
时间限制 Time Limitation
1s
第一种解题思路
初次读完这道题,我就在想:这不就是改改模板的事吗?(预示着我肯定会WA)
好了,废话不多说,说一下思路,我首先想到思路就是:用spfa求最短路径的同时,开一个d2数组维护一下次短路,最后直接输出d2【n】即可(即源点到第n点的次短路)。
#include<bits/stdc++.h>
using namespace std;
const int maxm=100010;
const int maxn=5010;
int n, m, tot=0;
long long d1[maxn], d2[maxn];
int head[maxn], next[maxm<<1], ver[maxm<<1], edge[maxm<<1];
inline int read()
{
int f=1,num=0;
char ch=getchar();
while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
while (ch>='0'&&ch<='9') num=(num<<1)+(num<<3)+ch-'0', ch=getchar();
return num*f;
}
void add(int x,int y,int z)
{
ver[++tot]=y,edge[tot]=z,next[tot]=head[x],head[x]=tot;
ver[++tot]=x,edge[tot]=z,next[tot]=head[y],head[y]=tot;
}
bool v[maxn];
void spfa()
{
queue<int>q;
memset(d1,0x3f,sizeof(d1));
memset(d2,0x3f,sizeof(d2));
v[1]=1,d1[1]=0;
q.push(1);
while (!q.empty())
{
int x=q.front();
q.pop();
v[x]=0;
for (int i=head[x];i;i=next[i])
{
int y=ver[i],z=edge[i];
if (d1[y]>d1[x]+z)
{
d2[y]=d1[y];
d1[y]=d1[x]+z;
if (!v[y]) q.push(y),v[y]=1;
}
else if (d1[y]<d1[x]+z&&d2[y]>d1[x]+z)//这条边不是最短路的边
{
d2[y]=d1[x]+z;
if (!v[y]) q.push(y),v[y]=1;
}
if (d2[y]>d2[x]+z)//注意不能else
{
d2[y]=d2[x]+z;
if (!v[y]) q.push(y),v[y]=1;
}
}
}
}
int main()
{
n=read(),m=read();
for (int i=1;i<=m;i++)
{
int x=read(),y=read(),z=read();
add(x,y,z);
}
spfa();
printf("%lld\n",d2[n]);
return 0;
}
写完自己想的代码后,一直WA,好无奈,上网借鉴一下大佬的写法,才发现我的数组开的太大了,导致运行时间太长。。。。。。。。。。。。。。。。。
错误的数组范围
const int maxnum=999999;
int n, m, tot, d1[maxnum], d2[maxnum], head[maxnum], next[maxnum], ver[maxnum], edge[maxnum];
下面是大佬的代码
(喜欢用边表的大佬们可以着重看一下)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
#define inf 1LL<<60
#define N 5010
#define M 100010
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,m,h[N],num=0;
ll d1[N],d2[N];
bool inq[N];
struct edge
{
int to,next,v;
}data[M<<1];
void spfa()
{
queue<int>q;
for(int i=1;i<=n;++i) d1[i]=d2[i]=inf;
q.push(1);
inq[1]=1;
d1[1]=0;
while(!q.empty())
{
int x=q.front();q.pop();inq[x]=0;
for(int i=h[x];i;i=data[i].next)
{
int y=data[i].to;
if(d1[x]+data[i].v<d1[y])
{
d2[y]=d1[y];d1[y]=d1[x]+data[i].v;
if(!inq[y]) q.push(y),inq[y]=1;
}
else if(d1[x]+data[i].v<d2[y]&&d1[x]+data[i].v>d1[y])
{//这条边不是最短路的边
d2[y]=d1[x]+data[i].v;
if(!inq[y]) q.push(y),inq[y]=1;
}
if(d2[x]+data[i].v<d2[y])
{//注意不能else
d2[y]=d2[x]+data[i].v;
if(!inq[y]) q.push(y),inq[y]=1;
}
}
}
}
int main()
{
// freopen("a.in","r",stdin);
n=read();m=read();
while(m--)
{
int x=read(),y=read(),v=read();
data[++num].to=y;data[num].next=h[x];h[x]=num;data[num].v=v;
data[++num].to=x;data[num].next=h[y];h[y]=num;data[num].v=v;
}
spfa();
printf("%lld\n",d2[n]);
return 0;
[这是大佬的博客,(毕竟参考了人家的代码)] https://blog.csdn.net/Icefox_zhx/article/details/77881676(https://blog.csdn.net/Icefox_zhx/article/details/77881676)
第二种思路
随后,发现了一位大佬说了:
次短路的做法好多吧……
可以在spfa的时候顺便维护个次短的长度
也可以正反各一遍最短路,然后枚举每一条边的dis[1][l]+dis[r][n]+v更新次短路(我就是这么做的)
还可以像k短路一样倒着跑最短路然后正着A*
。。。。。。。。。。。。。。。。。。。。。。。。。。。
所以在此奉上大佬做的那种代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int mxn=100005;
queue <int> q;
struct edge {int to,w,next;} f[mxn<<1];
bool vis[mxn];
int head[mxn],dis[mxn][2];
int n,m,cnt,ans=1e9,mn;
inline void add(int u,int v,int w)
{
f[++cnt].to=v,f[cnt].w=w,f[cnt].next=head[u],head[u]=cnt;
f[++cnt].to=u,f[cnt].w=w,f[cnt].next=head[v],head[v]=cnt;
}
inline void spfa(int s,int t,int k)
{
int i,j,u,v,w;
memset(vis,0,sizeof(vis));
dis[s][k]=0;
q.push(s);
while(!q.empty())
{
u=q.front();
q.pop();
vis[u]=0;
for(i=head[u];i;i=f[i].next)
{
v=f[i].to,w=f[i].w;
if(dis[v][k]>dis[u][k]+w)
{
dis[v][k]=dis[u][k]+w;
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}
mn=dis[t][k];
}
int main()
{
int i,j,u,v,w;
scanf("%d%d",&n,&m);
while(m--)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
memset(dis,0x3f,sizeof dis);
spfa(1,n,0),spfa(n,1,1); //第1次从起点跑,第二次从终点跑
for (u=1;u<=n;u++)
for(i=head[u];i;i=f[i].next)
{
v=f[i].to,w=f[i].w;
if(dis[u][0]+dis[v][1]+w>mn)
ans=min(ans,dis[u][0]+dis[v][1]+w);
}
printf("%d\n",ans);
return 0;
}
理由同上 https://blog.csdn.net/qq_38246319/article/details/69938878
至于第三种,。。。。。。。。。。。。。。。。还是希望有心人把他A掉吧!!!!!
一些感想
所以,最后,我觉得次短路的做法可以当做一个知识点记忆下来(当然前提是要搞明白代码是怎么来的)!共同努力吧!!!