Roadblocks——SPFA求次短路

34 篇文章 3 订阅
25 篇文章 1 订阅
该博客探讨了如何使用SPFA(Shortest Path Faster Algorithm)算法来找到从农场1到农场N的第二短路径,而非最短路径。文章通过一个具体的例子展示了如何变形SPFA,将距离存储为二维数组,分别记录最短路和次短路。作者提醒注意SPFA在该问题中的效率限制,并建议使用A*算法作为更优解。文中还给出了完整的SPFA代码实现。
摘要由CSDN通过智能技术生成

题目:

描述
贝茜把家搬到了一个小农场,但她常常回到FJ的农场去拜访她的朋友。贝茜很喜欢路边的风景,不想那么快地结束她的旅途,于是她每次回农场,都会选择第二短的路径,而不象我们所习惯的那样,选择最短路。 贝茜所在的乡村有R(1<=R<=100,000)条双向道路,每条路都联结了所有的N(1<=N<=5000)个农场中的某两个。贝茜居住在农场1,她的朋友们居住在农场N(即贝茜每次旅行的目的地)。 贝茜选择的第二短的路径中,可以包含任何一条在最短路中出现的道路,并且,一条路可以重复走多次。当然咯,第二短路的长度必须严格大于最短路(可能有多条)的长度,但它的长度必须不大于所有除最短路外的路径的长度。

输入

  • 第1行: 两个整数,N和R,用空格隔开
  • 第2…R+1行: 每行包含三个用空格隔开的整数A、B和D,表示存在一条长度为 D(1 <= D <= 5000)的路连接农场A和农场B

输出

  • 第1行: 输出一个整数,即从农场1到农场N的第二短路的长度

样例
输入
4 4
1 2 100
2 4 200
2 3 250
3 4 100

输出
450
提示
//最短路:1 -> 2 -> 4 (长度为100+200=300)
第二短路:1 -> 2 -> 3 -> 4 (长度为100+250+100=450)

这道题要求次短路,我们可以选择A*,不会SPFA来暴力做,但是稍有不慎就会超时,每一点时间都不要浪费。

SPFA本来是用来求最短路的,那我们就来变形,把dist改为二维的,第一维存放最短路,第二维放次短路。
如过当前求到的最短路 d i s t [ v ] [ 0 ] + e d g e [ i ] dist[v][0]+edge[i] dist[v][0]+edge[i]可以小于以前求到的最短路 d i s t [ n o w ] [ 0 ] dist[now][0] dist[now][0],那我们就现将现在这个点的次短路也就是以前的最短路更新为次短路的值,再更新最短路的值。

dst[v][1]=dst[v][0];
dst[v][0]=dst[now][0]+eg[i];

如过当前求到的值比以前最短路的值大 d i s t [ v ] [ 0 ] + e d g e [ i ] < d i s t [ n o w ] [ 0 ] dist[v][0]+edge[i]<dist[now][0] dist[v][0]+edge[i]<dist[now][0],但却又比次短路的值小,我们就将其更新为次短路的值 d i s t [ v ] [ 1 ] > d i s t [ n o w ] [ 0 ] + e d g e [ i ] dist[v][1]>dist[now][0]+edge[i] dist[v][1]>dist[now][0]+edge[i],我们就直接将其更新为次短路的值

dst[v][1]=dst[now][0]+eg[i];

如过当前求到的值不大于以前的最短路但却又比以前的次短路大 d i s t [ v ] [ 1 ] > d i s t [ n o w ] [ 1 ] + e d g e [ i ] dist[v][1]>dist[now][1]+edge[i] dist[v][1]>dist[now][1]+edge[i],我们就将以前的次短路直接更新为求到的值.

dst[v][1]=dst[now][1]+eg[i];

还有就是要注意一下这道题用SPFA有点勉强,正解还是A*,因为代码里面会用到memset,所以数组也要卡着开,建议用快读或scanf,不要用cin,我就深受其害

下面是完整代码:

#include <bits/stdc++.h>
using namespace std;
/*上面已经讲了核心判断部分,其余和SPFA差不多,
所以就不注释代码了,起始主要还是我懒,~逃*/
int n,m,vis[5010],dst[5010][2],head[5010],nxt[200010],to[200010],tot,eg[200010];

int read() {
	int sum=0,fg=1;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-')fg=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		sum=sum*10+c-'0';
		c=getchar();
	}
	return sum*fg;
}

void add(int x,int y,int c) {
	tot++;
	nxt[tot]=head[x];
	to[tot]=y;
	eg[tot]=c;
	head[x]=tot;
}

void spfa(int s) {
	memset(dst,0x3f3f3f,sizeof(dst));
	dst[s][0]=0;
//	dst[1][1]=0;
	vis[s]=0;
	queue<int>q;
	q.push(s);
	while(!q.empty()) {
		int now=q.front();
		q.pop();
		vis[now]=0;
		for(int i=head[now]; i; i=nxt[i]) {
			int v=to[i];
			if(dst[v][0]>dst[now][0]+eg[i]) {
				dst[v][1]=dst[v][0];
				dst[v][0]=dst[now][0]+eg[i];
				if(!vis[v]) {
					q.push(v);
					vis[v]=1;
				}

			}
			if(dst[v][1]>dst[now][0]+eg[i]&&dst[v][0]<dst[now][0]+eg[i]) {
				dst[v][1]=dst[now][0]+eg[i];
				if(!vis[v]) {
					q.push(v);
					vis[v]=1;
				}
			}
			if(dst[v][1]>dst[now][1]+eg[i]) {
				dst[v][1]=dst[now][1]+eg[i];
				if(!vis[v]) {
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
}

int main() {
	n=read();
	m=read();
	for(int i=1; i<=m; i++) {
		int a,b,c;
		a=read();
		b=read();
		c=read();
		add(a,b,c);
		add(b,a,c);
	}
	spfa(n);
	cout<<dst[1][1];

	return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值