单源最短路-SPFA版本 学习笔记

关于SPFA

它死了。

NOI2018第一题 归程,很多选手使用了一种广为人知一种求最短路的算法——SPFA,最后,由于SPFA算法被造数据的人卡掉了,结果:
\(100 \rightarrow 60\)
\(Ag \rightarrow Cu\)
希望大家不要重蹈覆辙。

这一题的正解是\(Dijstra\),但是,我们先讲讲SPAFA,毕竟是我还是一个普及组的蒟蒻,而普及组不会被卡SPFA。
关于Dijstra的题解可以点击这里

算法实质

关于SPFA,其实就是一个松弛计划。说白了,就是这样的:

graph LR A((1))--2-->B((2)) B--4-->C((3)) A--3-->C

首先令\(dist_i\)为第\(i\)个点到\(1\)号点的最短路,那么,\(dist_1=0\)
接下来我们遍历这张图(这里用dfs,但代码一般是用bfs更快)。
先遍历\(2\)号点,那么\(dist_2=2\)。然后继续\(dist_3=6\)就可以了。
接下来是重点我们回到\(3\)号点这是3号点已经有值了,但是原来的\(dist_3\)的值是\(6\),但是,我们遍历过来的时候的值为\(3(dist_1+3)\),比原来的\(dist_3\)小,这是我们就可以把\(dist_3=3\)就可以了,这样这张图的遍历就完成了。

算法缺点

如果出题人有意造数据,你遍历了这张图,先从第一个点开始遍历,然后遍历完这张图,把所有的点都得到了一个值,然后回来发现,这个值都是加的,这样就会浪费掉大量的时间,就会被卡成\(O(N\times M)\)的复杂度了,接下来就是TLE了,所以,这里建议使用广度优先搜索。

代码实现

这里用链式前向星来存图以及BFS。
关于BFS

#include<cstdio>//SPAFA
#include<queue>//采用链式前向星 
#include<cstring>//BFS
#define maxn 500039 
using namespace std;
int to[maxn],nex[maxn],head[maxn],q[maxn],k;
int n,m,i,f,t,s,qq;
int ans[maxn];
void add(int x,int y,int z){
	to[k]=y; q[k]=z;
	nex[k]=head[x];
	head[x]=k;
	k++;
	return;
}
void bfs(int x){
	queue<int> qq;
	int now; ans[x]=0;
	qq.push(x);
	while(!qq.empty()){
		int cur=qq.front(); qq.pop();
		for(int i=head[cur];i!=-1;i=nex[i]){
			if( ans[cur] + q[i] >= ans[to[i]] )
			    continue;
			ans[to[i]] = ans[cur] + q[i];
			qq.push(to[i]); 
		}
	}
	return;
}
int main(){
    scanf("%d%d%d",&m,&n,&s);
    memset(ans,0x3f,sizeof(ans));
    memset(head,-1,sizeof(head));
    memset(nex,-1,sizeof(nex));
    ans[s]=0;
    for(i=1;i<=n;i++){
    	scanf("%d%d%d",&f,&t,&qq);
    	add(f,t,qq);
	}
	bfs(s);
	for(int i=1;i<=m;i++)
	    if(ans[i]==0x3f3f3f3f)
	        printf("2147483647 ");
	    else printf("%d ",ans[i]);
	
    return 0;
}
posted @ 2021-02-15 21:15  jiangtaizhe001  阅读(71)  评论(0)    收藏  举报