SPFA Shortest Path Faster Algorithm 最短路径最快算法

算法思想

SPFA 算法是 Bellman-Ford算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。SPFA 最坏情况下复杂度和朴素 Bellman-Ford 相同,为 O(VE)。

最简单的模板

#define pii pair<int,int>
vector<pii> a[maxn]; //邻接表,存图
int dis[maxn]; //距离 void spfa(int s) //s源点
{
mem(dis,inf); dis[s] = 0;
queue<int> q;q.push(s);
while (!q.empty())
{
s = q.front();q.pop();
for(auto i : a[s])
{
if(dis[i.first] > dis[s]+i.second)
{
dis[i.first] = dis[s]+i.second;
q.push(i.first);
}
}
}
}

判断是否存在负环

int dis[maxn];
bool vis[maxn]; int out[maxn]; //出队次数 bool spfa(int s)
{
mem(dis,inf);dis[s] = 0;
queue<int> q;q.push(s);
while(!q.empty())
{
s = q.front();q.pop();vis[s] = 0;
out[s]++;if(out[s] >= n) return false; //存在负环
for(auto i : a[s])
{
if(dis[i.first] > dis[s]+i.second)
{
dis[i.first] = dis[s]+i.second;
if(!vis[i.first])
{
vis[i.first] = 1;
q.push(i.first);
}
}
}
}
return true; //不存在负环
}

SLF优化

SLF优化,即 Small Label First 策略,使用 双端队列 进行优化。也可以手写双端队列

一般可以优化15%~20%,在竞赛中比较常用。

设从 u 扩展出了 v ,队列中队首元素为 k ,若 dis[ v ] < dis[ k ] ,则将 v 插入队首,否则插入队尾。

注:队列为空时直接插入队尾。

#define pii pair<int,int>
vector<pii> a[maxn]; //邻接表,存图
int dis[maxn]; //距离
bool vis[maxn]; //标记,表示该点当前是否在队列中,在的话就不用进队了 void spfa_slf(int s)
{
mem(dis,inf); dis[s] = 0;
deque <int> q; //正常队列新元素插入队尾,双端队列可以选择
q.push_back(s);
while(!q.empty())
{
s = q.front(); //队首
q.pop_front(); //队首pop
vis[s] = 0;
for(auto i : a[s])
{
if(!vis[i.first] && dis[i.first] > dis[s]+i.second)
{
vis[i.first] = 1;
dis[i.first] = dis[s]+i.second;
if(!q.empty() && dis[q.front()] > dis[i.first]) q.push_front(i.first); //插入队首
else q.push_back(i.first); //否则插入队尾
}
}
}
}

LLL优化

LLL优化,即 Large Label Last 策略,使用 双端队列 进行优化。

一般用SLF+LLL可以优化50%左右,但是在竞赛中并不常用LLL优化。

设队首元素为 k ,每次松弛时进行判断,队列中所有 dis 值的平均值为 x 。

若 dist[ k ] > x ,则将 k 插入到队尾,查找下一元素,直到找到某一个 k 使得 dis[ k ] <= x ,则将 k 出队进行松弛操作。

void spfa_lll(int s)
{
for(int i = 1;i <= n; i++) dis[i] = inf;
dis[s] = 0;
deque<int> q;
q.push_back(s);
ll x = 0; int num = 1; //x是队列元素的和,num是队列元素的个数
while(!q.empty())
{
s = q.front();
q.pop_front();
num--; x -= dis[s]; while (num && dis[s] > x/num) //和slf的唯一区别
{
q.push_back(s); //就把s放到队尾 s = q.front(); //另取s
q.pop_front();
} vis[s] = 0;
for(auto i : a[s])
{
if(!vis[i.first] && dis[i.first] > dis[s]+i.second)
{
dis[i.first] = dis[s]+i.second;
vis[i.first] = 1; if(!q.empty() && dis[i.first] > dis[q.front()]) q.push_front(i.first);
else q.push_back(i.first); num++,x += dis[i.first];
}
}
}
}

swap优化

每当队列改变时,如果队首距离大于队尾,则交换首尾。

void spfa(int s)                //swap
{
for(int i = 1;i <= n; i++) dis[i] = 1e18;
dis[s] = 0; int l = 1e7,r = 1e7; //l是队首,r是队尾
q[l] = s; while(l <= r)
{
s = q[l]; l++;
vis[s] = 0; //标记出队 for(auto i : a[s])
{
if(dis[i.first] > dis[s]+i.second)
{
dis[i.first] = dis[s]+i.second;
if(!vis[i.first]) //没必要重复进队
{
vis[i.first] = 1;
q[--l] = i.first;
if(l < r && dis[q[l]] > dis[q[r]]) swap(q[l],q[r]);
}
}
}
}
}

例题

参考博客

百度百科

https://www.cnblogs.com/Yangrui-Blog/p/8997721.html

https://www.cnblogs.com/dilthey/p/9583728.html

最短路--SPFA及其优化的更多相关文章

  1. 最短路--spfa+队列优化模板

    spfa普通版就不写了,优化还是要的昂,spfa是可以判负环,接受负权边和重边的,判断负环只需要另开一个数组记录每个结点的入队次数,当有任意一个结点入队大于点数就表明有负环存在 #include< ...

  2. C++-POJ1502-MPI Maelstrom-[最短路][spfa][栈优化]

    我不理解为什么写dijkska就WA呢? atoi()是个好东西,给你个颜色,自己体会 疑惑!疑惑!疑惑! #include <queue> #include <cstdio> ...

  3. 最短路模板(Dijkstra & Dijkstra算法+堆优化 & bellman_ford & 单源最短路SPFA)

    关于几个的区别和联系:http://www.cnblogs.com/zswbky/p/5432353.html d.每组的第一行是三个整数T,S和D,表示有T条路,和草儿家相邻的城市的有S个(草儿家到 ...

  4. SPFA队列优化

    spfa队列优化(用来求最短路) 实现方法: 1.存入图.可以使用链式前向星或者vocter. 2.开一个队列,先将开始的节点放入. 3.每次从队列中取出一个节点X,遍历与X相通的Y节点,查询比对   ...

  5. SPFA 小优化*2

    /* bzoj 2763 SPFA小优化 循环队列+SLF 顺面改掉自己之前手打qeueu的坏毛病*/ #include<iostream> #include<cstring> ...

  6. HDU 1535 Invitation Cards(SPFA,及其优化)

    题意: 有编号1-P的站点, 有Q条公交车路线,公交车路线只从一个起点站直接到达终点站,是单向的,每条路线有它自己的车费. 有P个人早上从1出发,他们要到达每一个公交站点, 然后到了晚上再返回点1. ...

  7. [BZOJ 2200][Usaco2011 Jan]道路和航线 spfa+SLF优化

    Description Farmer John正在一个新的销售区域对他的牛奶销售方案进行调查.他想把牛奶送到T个城镇 (1 <= T <= 25,000),编号为1T.这些城镇之间通过R条 ...

  8. 【最短路径】 SPFA算法优化

    首先先明确一个问题,SPFA是什么?(不会看什么看,一边学去,传送门),SPFA是bellman-ford的队列优化版本,只有在国内才流行SPFA这个名字,大多数人就只知道SPFA就是一个顶尖的高效算 ...

  9. L - Subway(最短路spfa)

    L - Subway(最短路spfa) You have just moved from a quiet Waterloo neighbourhood to a big, noisy city. In ...

随机推荐

  1. CMakeLists 添加 -pthread 编译选项 undefined reference to pthread_atfork

    在与 main() 函数同级的 CMakeLists 中添加如下内容(根据项目实际情况修改): cmake_minimum_required (VERSION 2.6) find_package (T ...

  2. php中array_replace,array_splice和str_replace三个函数相互比较

    php中有一些功能相似或者是名称相似的函数,比如array_replace,array_splice和str_replace这三个函数,从名称来看前两个操作数组的,后一个操作字符串的. array_r ...

  3. Python进阶:生成器--懒人版本的迭代器

    从容器.可迭代对象谈起 所有的容器都是可迭代的(iterable),迭代器提供了一个next方法.iter()返回一个迭代器,通过next()函数可以实现遍历. def is_iterable(par ...

  4. go String接口方法

    该接口经常用于输出 struct 的值 或者记录struct数据日志 一个普遍存在的接口是 fmt 包中定义的 Stringer接口 发现 http://tour.studygolang.com/me ...

  5. Filter讲解4

    想要 浏览更多Fiddler内容:请点击进入Fiddler官方文档 阅读目录: 一.使用.NET代码扩展Fiddler 二.实现Fiddler接口 三.创建Fiddler扩展项目 四.在扩展程序选项卡 ...

  6. Spring Cloud Alibaba学习笔记(10) - Spring消息编程模型下,使用RocketMQ收发消息

    编写生产者 集成 添加依赖 <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId&g ...

  7. jmeter接口测试中的用例数据分离

    用jmeter做接口测试的话,一个jmx文件就可以是一个用例,而用例的设计多数还是等价类.边界值等方法.用例越来越多的时候,维护比较麻烦,所以可以把用例的数据存在csv文件中,然后通过组件(CSV D ...

  8. 在论坛中出现的比较难的sql问题:38(字符拆分 字符串检索问题)

    原文:在论坛中出现的比较难的sql问题:38(字符拆分 字符串检索问题) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所以,觉得 ...

  9. Python之(matplotlib、numpy、pandas)数据分析

    一.Matplotlib 是一个 Python 的 2D绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形. 它主要用来回事图形,用来展现一些数据,更加直观的展示,让你第一眼就只要数 ...

  10. Python:Shapefile矢量转化为GeoJSON格式

    在最近的项目中,完成了许多python处理矢量数据的算法程序,比如缓冲区分析.叠置分析.统计分析等,主要用到的是GDAL/OGR库,很多功能都参照了此链接中的示例:http://pcjericks.g ...