P1220 第二短路

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/83757458

题目

描述 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)

第二种思路

随后,发现了一位大佬说了:
次短路的做法好多吧……

扫描二维码关注公众号,回复: 4210026 查看本文章

可以在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掉吧!!!!!

一些感想

所以,最后,我觉得次短路的做法可以当做一个知识点记忆下来(当然前提是要搞明白代码是怎么来的)!共同努力吧!!!

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/83757458