SPFA ------队列优化的Bellman-Ford

由Bellman-Ford算法实现带有负权边的单源最短路,时间复杂度是O(VE),也就是边数乘顶点数。但是根据Bellman-Ford的状态转移方程$$dist[i] = min(dist[i] , last[k] + w[k -> i])$$可知,当且仅当顶点k的dist值在上一层有更新时,在本层更新时顶点 i 的dist值才有可能变小。因此SPFA出现了

SPFA算法相较于Bellman-Ford多维护了一个队列,队列中存储可能使得最短路获得更新的顶点。每次更新我们仅使用队列中的顶点来进行更新,同时每次将更新过的顶点加入队列,这样就避免了无用的更新操作。

SPFA求带负权边的单源最短路

给出例题851. spfa求最短路 - AcWing题库

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int N = 1e5+20;
int n,m;
vector<pair<int,int>> g[N];
int dist[N];
bool st[N]; //判断该顶点是否已经在队列中,防止重复入队
void spfa()
{
memset(dist,0x3f,sizeof dist);
dist[1] = 0;
queue<int> q;
q.push(1); //将源点加入队列中
st[1] = true;
while(q.size())
{
int k = q.front();
q.pop();
st[k] = false; //出队后状态置为false
for(auto x : g[k])
{
int v = x.first, w = x.second;
if(dist[v] > dist[k] + w)
{
dist[v] = dist[k] + w;
if(!st[v]) //防止重复入队
{
q.push(v);
st[v] = true;
}
}
}
} }
int main()
{
cin >> n >> m;
while(m --)
{
int x,y,z;
cin >> x >> y >> z;
g[x].push_back({y,z});
}
spfa();
if(dist[n] > 0x3f3f3f3f/2) puts("impossible");
else cout << dist[n];
return 0;
}
SPFA判断图中是否有负环

和Bellman-Ford一样,当图中出现负环时(如下图)

flowchart LR
A((A)) -->|2| B((B))
B -->|-5| C((C))
C -->|1| A

根据从源点到目标点的最短路至多经过n-1条边,当存在负环时,更新操作可以一直进行,也就是说边数会大于n-1。我们只需要监视在更新过程中是否出现经过边数大于n-1即可。

如下题

852. spfa判断负环 - AcWing题库

代码如下

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int N = 2500;
vector<pair<int,int>> g[N];
int dist[N];
int st[N],cnt[N];
int n,m;
bool spfa()
{
queue<int> q;
for(int i = 1 ; i <= n ; i++)
{
q.push(i);
st[i] = true;
}
while(q.size())
{
int k = q.front();
q.pop();
st[k] = false;
for(auto x : g[k])
{
int v = x.first, w = x.second;
if(dist[v] > dist[k] + w)
{
dist[v] = dist[k] + w;
cnt[v] = cnt[k] + 1;
if(cnt[v] >= n)
return true;
if(!st[v])
{
q.push(v);
st[v] = true;
}
}
}
}
return false;
}
int main()
{
cin >> n >>m;
while(m --)
{
int x,y,z;
cin >> x >> y >> z;
g[x].push_back({y,z});
}
if(spfa())
cout << "Yes";
else
cout << "No";
return 0;
}

几点细节

  1. 这里开始将所有顶点都入队,是因为若仅使用一个源点的话,出现孤立的不连通块会使得我们在找负环或者说更新最短路时有疏漏。因此初始将所有顶点加入队列,且不用初始化dist数组。因为我们目标是找出负值圈,而如果有负值圈,最短路会小于0且会一直更新,也就是说队列永远不会空,且dist会一直减小。我们只需要在某个节点检测到cnt[v] >= n即可
  2. 以上将所有顶点入队的操作也可以理解为有一个虚拟的源点s,其与图中各个点都有一条权值为0的点,也就说该虚拟源点使得图联通起来。然后使用该虚拟源点更新就是将各个顶点加入队列。
  3. 关于cnt[v] = cnt[k] + 1。显然若k顶点能够更新dist[v],那么顶点v的最短路所经过的边数即为顶点k所经过的边数加上更新的这条边数。

SPFA -----队列优化的Bellman-Ford的更多相关文章

  1. SPFA队列优化

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

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

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

  3. poj1860 bellman—ford队列优化 Currency Exchange

    Currency Exchange Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 22123   Accepted: 799 ...

  4. 关于SPFA的双端队列优化

    7.11 Update 我做题的时候发现这样写会RE 因为在使用双端队列优化SPFA的时候 在将一个点加入队列的时候,如果队列已经空了 那么一旦出现dis[Q.front()]就会RE 可以这样修改 ...

  5. HDU 2544 最短路(floyd+bellman-ford+spfa+dijkstra队列优化)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2544 题目大意:找点1到点n的最短路(无向图) 练一下最短路... dijkstra+队列优化: #i ...

  6. 队列优化dijsktra(SPFA)的玄学优化

    转载:大佬博客 最近想到了许多优化spfa的方法,这里想写个日报与大家探讨下 前置知识:spfa(不带任何优化) 由于使用较多 STLSTL ,本文中所有代码的评测均开启 O_2O2​ 优化 对一些数 ...

  7. poj 3259 Wormholes : spfa 双端队列优化 判负环 O(k*E)

    /** problem: http://poj.org/problem?id=3259 spfa判负环: 当有个点被松弛了n次,则这个点必定为负环中的一个点(n为点的个数) spfa双端队列优化: 维 ...

  8. Bellman-Ford算法及其队列优化(SPFA)

    一.算法概述 Bellman-Ford算法解决的是一般情况下的单源最短路径问题.所谓单源最短路径问题:给定一个图G=(V,E),我们希望找到从给定源结点s属于V到每个结点v属于V的最短路径.单源最短路 ...

  9. SPFA(Bellman-Ford队列优化)

    原理:队列+松弛操作 将源点加入队尾,每一步读取队头顶点u,并将队头顶点u出队(记得消除标记):将与点u相连的所有点v进行松弛操作,如果能更新距离(即令d[v]变小),那么就更新,另外,如果点v没有在 ...

  10. 图论之最短路径(3)队列优化的Bellman-Ford算法(SPFA算法)

    在Bellman-Ford算法中 我们可以看到大量的优化空间:如果一个点的最短路径已经确定了,那么它就不会再改变,因此不需要再处理.换句话说:我们每次只对最短路径改变了的顶点的所有出边进行操作 使用一 ...

随机推荐

  1. iviews Radio组件如何获取key而不是value

    iviews RadioGroup 官网里介绍Radio组件获取的值都是name属性没有value 例如: <template> <Space wrap size="lar ...

  2. plsqll连接Oracle的两种方式

    第一种方式:配置tnsnames.ora 找到plsql软件根目录 下的配置文件

  3. Angular系列教程之父子组件通信详解

    .markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...

  4. AXI Channel

    AXI Channel axi与ahb不同就是分为不同的channel write address channel - 表明一个transaction基本的属性,包含本次传输的地址\类型\大小(多少字 ...

  5. c# 编写 WebAssembly

    创建一个.net 7.0类库工程,引用下面的nuget包: <PackageReference Include="Microsoft.AspNetCore.Components.Web ...

  6. Git-历史版本切换-log-reset

  7. [转帖]深入理解mysql-第十一章 mysql查询优化-Explain 详解(中)

    一.执行计划-type属性 执行计划的一条记录就代表着MySQL对某个表的执行查询时的访问方法,其中的type列就表明了这个访问这个单表的方法具体是什么,比方说下边这个查询: mysql> EX ...

  8. [转帖]关于gdb相关的几个工具的说明

    https://phpor.net/blog/post/846 使用rpm命名查看gdb的rpm包,主要由下面几个程序:/usr/bin/gcore/usr/bin/gdb/usr/bin/gdbse ...

  9. [转帖]服务器稳定性测试-LTP压力测试方法及工具下载

    简介 LTP(LinuxTest Project)是SGI.IBM.OSDL和Bull合作的项目,目的是为开源社区提供一个测试套件,用来验证Linux系统可靠性.健壮性和稳定性.LTP测试套件是测试L ...

  10. [转载]Linux常用的可插拔认证模块(PAM)pam_limits.so、pam_rootok.so和pam_userdb.so的详解

    Linux常用的可插拔认证模块(PAM)pam_limits.so.pam_rootok.so和pam_userdb.so的详解 https://blog.51cto.com/udb1680/1846 ...