0721-简单易懂的SPFA模板-洛谷3371

 代码 

(先上代码,后讲思想)

#include
#include
#include
#include
#include
#include
#define N 10009
#define M 500009
using namespace std;
int dis[N],nxt[M],to[M],w[M],head[N];
int n,m,s;
queue q;
inline int read(){
	char ch;
	int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9')
		if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9')
	{
		res=(res<<1)+(res<<3)+(ch^48);//注意位运算的优先级别
		ch=getchar();
	}
	return f*res;
}
int tot=0;
bool vis[N];
void add(int x,int y,int z){
	nxt[++tot]=head[x];
	head[x]=tot;
	w[tot]=z;
	to[tot]=y;
}
void spfa(int u){
	q.push(u);
	vis[u]=1;
	while(!q.empty()){
		int x=q.front();
		q.pop();
		vis[x]=0;
		for(int i=head[x];i;i=nxt[i]){
			int y=to[i];
			if(dis[x]+w[i]

基本思想

队列优化版的Bellman-Ford

先把起点加进队列中,然后松弛和起点相连的所有边,如果松弛成功且该点不在队列中,那么就把这个点入队。然后依次取出队列中的每一个点进行松弛,直到队列为空


为什么可以这样松弛,进行优化呢?

首先我们要明确Bellman-Ford算法是有很多次浪费的松弛的(也就是该次松弛是不可能成功的),因为对于一条边如果他连接的是x 和 y (有向,从x 到 y ),而x还没被松弛,则y 也不可能被松弛,所以就有了我们改进版的SPFA

(顺便讲一下,为什么Bellman-Ford算法,松弛(n - 1)即可,因为我看到好几篇博客都没解释)

对于每一个点,从起点到它至多经过(n - 2 )个点,那么最多会被(n - 1 )条边松弛


注意事项

SPFA可以处理有负权的最短路,却不能处理带负权回路的(就是指一个环,其权值总和为负数)

因为这样的话,每走一次这个环,很多相关的点的路径就会变短,就会又有很多点入队,然后不停的入队,出队,就被卡死了,而最短路会无穷小。当然,这种情况我们需要告诉计算机这组数据无解,好让他退出死循环。基于一条性质“每一个节点最多被松弛(n - 1)次”   我们就可以在某节点松弛次数大于(n - 1)时,退出循环

(至于为什么是(n - 1)次,蒟蒻表示并不知道,求路过的大佬指点)

来一组样例自己推一下就搞懂了

4   4   1    (4个点,4条边,1为起点)

1   2   2

2   3   1

3   4   -5

4   1   -3

所以的所以,会有些毒瘤的出题人,专门出数据来卡你(但基本上不会有啦,所以放心使用)

至于怎么在此基础上求最短路,我也不清楚,但我知道怎么快速判断是否有负权回路的出现,这就是下次要讲的拓展部分了

(P.S 蒟蒻都知道如果一个图中出现了负权回路,那么这个图是不存在最短路的)

你可能感兴趣的:(SPFA)