复习之路~最短路算法Spfa

前言

本文所有内容出自于个人的理解,可能有谬误,还请多多包涵,敬请指正。qq :2039316792
本文中参考的所有文章,将会在专页中一一列出。

目录

  1. Dijkstra
  2. Spfa
  3. Floyd

正文

1、Dijkstra

2、Spfa

概论

Spfa是一种可以处理有负权边的图的算法,也能处理有负权环的图,但是Spfa十分玄学,对于菊花图,网格图等特殊的图,便会退化为Bellman-Ford算法。其思想为动态逼近,写法十分类似BFS 。

基本流程

  1. 初始化dis[每一个点]=inf,dis[起点]=0。
  2. 将起点压入队列,标记起点在队列中将起点压入队列,标记起点在队列中
  3. 取出队首,取消队首的标记,遍历队首可扩展的边,若存在dis[边的另一个点]>dis[队首]+该边边权,那么更新dis。
  4. 若边的另一个点不在队列中,那么标记改点并将该点压入队列。
  5. 若此时队列不为空,则继续执行语句3.
    PS:若要判负环,则只要在语句4中判断某一个点出现的次数是否大于n+2。(也可以是n+1,个人认为n+2更为稳妥)

代码实现

PS:以下代码以洛谷 P3371 【模板】单源最短路径(弱化版)为例

#include<bits/stdc++.h>
#define inf 2147483647
#define maxn 100000
using namespace std;
struct edge
{
	int nxt,to,w;
}p[maxn*50];
int n,m,s,cnt;
int head[maxn],dis[maxn];
bool vis[maxn];
void add(int u,int v,int w)
{
	p[++cnt].nxt=head[u];
	p[cnt].to=v;
	p[cnt].w=w;
	head[u]=cnt;
}
void spfa()
{
	queue< int >q;
	for(int i=1;i<=n;i++)dis[i]=inf;
	dis[s]=0;
	vis[s]=1;
	q.push(s);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		vis[x]=0;
		for(int i=head[x];i;i=p[i].nxt)
		{
			int to=p[i].to;
			if(dis[to]>dis[x]+p[i].w)
			{
				dis[to]=dis[x]+p[i].w;
				if(!vis[to])
				{
					vis[to]=1;
					q.push(to);
				}
			}
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&s);
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
	}
	spfa();
	for(int i=1;i<=n;i++)printf("%d ",dis[i]);
	return 0;
}

优化

SLF:Small Label First 策略

设即将压入双端队列的点为是j,队首为i,若dis[j]<dis[i]则将j压入队首,否则压入队尾。

LLL:Large Label Last 策略

设队首为i,即将压入双端队列的点为j,则有 x = i = 1 n d i s i n ( d i s ) x=\frac{\sum_{i=1}^n dis_i}{n}(队列中所有dis值的平均值)
若dis[j]>x,则将点j压入队尾。
代码我先欠着。。。。先更新Dijsktra的GIF。。。。

3、Floyd

参考文献

  1. 为什么Dijkstra算法不适用边长为负数的情况
  2. spfa的SLF 和 LLL优化算法

福利

在这里插入图片描述
还不更╭(╯^╰)╮

猜你喜欢

转载自blog.csdn.net/Loi_magic/article/details/82980954