最短路算法SPFA

合集下载

最短路问题的求解方法

最短路问题的求解方法

最短路问题的求解方法最短路问题是图论中的一个经典问题,它在很多实际应用中都有着重要的作用。

在现实生活中,我们经常需要求解最短路径,比如在地图导航、网络通信、交通运输等领域。

因此,研究最短路问题的求解方法具有重要的理论意义和实际应用价值。

在图论中,最短路问题的求解方法有很多种,其中比较经典的有Dijkstra算法、Bellman-Ford算法、Floyd-Warshall算法等。

这些算法各有特点,适用于不同的场景和要求。

下面我们就逐一介绍这些算法的原理和求解方法。

Dijkstra算法是一种用于求解单源最短路径的算法,它采用贪心策略,每次找到当前距离最短的节点进行松弛操作,直到所有节点都被遍历。

Dijkstra算法的时间复杂度为O(V^2),其中V为节点的个数。

这种算法适用于边权值为正的图,可以求解从单个源点到其他所有点的最短路径。

Bellman-Ford算法是一种用于求解单源最短路径的算法,它可以处理边权值为负的图,并且可以检测负权回路。

Bellman-Ford算法的时间复杂度为O(VE),其中V为节点的个数,E为边的个数。

这种算法适用于一般情况下的最短路径求解,但是由于其时间复杂度较高,不适用于大规模图的求解。

Floyd-Warshall算法是一种用于求解所有点对最短路径的算法,它可以处理边权值为正或负的图,但是不能检测负权回路。

Floyd-Warshall算法的时间复杂度为O(V^3),其中V为节点的个数。

这种算法适用于求解图中所有点对之间的最短路径,可以同时求解多个源点到多个目标点的最短路径。

除了上述几种经典的最短路求解算法外,还有一些其他的方法,比如A算法、SPFA算法等。

这些算法在不同的场景和要求下有着各自的优势和局限性,需要根据具体情况进行选择和应用。

在实际应用中,最短路问题的求解方法需要根据具体的场景和要求进行选择,需要综合考虑图的规模、边权值的情况、时间效率等因素。

同时,对于大规模图的求解,还需要考虑算法的优化和并行化问题,以提高求解效率。

SPFA算法

SPFA算法

让我们结合一道题目来进行探讨
苹果争夺战 两个人A,B在一个5*6的矩阵里抢夺苹果。矩阵包含空地,4棵 苹果树和障碍物,每个苹果树上有3个苹果。A先行动,然后两人轮 流操作,每一回合每人可以向四周移动一格或停留在一棵苹果树下, 如果苹果树非空可以摘下一个苹果。 两人不能移动到矩阵外,障碍物上或是对方的位置,且两人绝 顶聪明。 问A最多可以抢到多少个苹果。
SPFA的核心正是松弛操作:
Relax(u,v){
If (F(v)>F(u)+W_Cost(u,v)) F(v)=F(u)+W_Cost (u,v);
}
但松弛操作直接得出的Bellman-Ford算法效率低下
For Time=1 to N For (u,v)∈E Relax(u,v)
S A1
.......
我们再从另一个角度来分析DIJ和spfa
Dij的思想可以从另一个角度来阐述:一个
点的最短路必定从另一个点的最短路推导 出,而另一个点的最短路必定<这个点的最 短路,因此我们可以DP,但是因为存在环, 所以就只能从最短路最小的那个开始推, 所以只要满足求的是最短路,都可以用DIJ。
我们再从另一个角度来分析DIJ和spfa
类似于在负权图上使用Dijikstra
因此标号法并不适用
思路二:参考负权图上求最短路的思想
通过局部的较优值一步步迭代得到最优解
假设当前解为:
G[ ]= 4 5
F[ ]= 3 5
之后G[ ]得出最优解4
问题所在:F[ ]和G[ ]的最优化目标不同
两种常规解法都失败了,我们需要从新的角度来思考 猜想: 能否越过状态间纷繁复杂的转移关系 直接考虑最终状态呢?
当某边三角不等式不成立时,用松弛操作调整之。

SPFA模板

SPFA模板

SPFA模板SPFA :求⼀个点到其他所有点的最短路,时间快。

队列处理,队头元素所有相邻的点进⾏松弛,若某个相邻的点松弛成功,则将其⼊队。

直到队列为空时算法结束。

例题 hdu 2680代码:1 #include<bits/stdc++.h>2using namespace std;3#define M 10094#define INF 0x3f3f3f3f5struct edge6 {7int to,w;//保存边的信息,包括边的终点和权值8 };9int dis[M]; //最短距离10bool vis[M]; //标记该点是否在队列11 vector<edge> g[M]; //利⽤⼀个vector保存,g[i]表⽰以i为起点的所有边的信息12int n,m,ee;13void spfa(int u)14 {15for(int i = 0;i <= n;i++) //初始化16 {17 dis[i] = INF;18 vis[i] = false;19 }20 dis[u] = 0;21 vis[u] = true; //加⼊队列,标记22 queue<int> q;23 q.push(u);24while(!q.empty())25 {26 u = q.front();27 vis[u] = false;28 q.pop();29for(int i = 0;i < g[u].size();i++)30 {31int v = g[u][i].to; //找出这条边的终点32int w = g[u][i].w; //边的权值33if(dis[v] > dis[u]+w) //如果终点的最短距离⽐起点的最短距离加上这条边的权值那么就更新34 {35 dis[v] = dis[u]+w;36if(!vis[v]) //如果v点的最短距离有所更新并且不在队列中,就将其加⼊队列。

由三村最短路问题两个解法引发的推广

由三村最短路问题两个解法引发的推广

由三村最短路问题两个解法引发的推广
三村最短路问题是一道经典的图论问题,其两个解法——Dijkstra算法和SPFA算法是求解最短路问题中最常见的两种算法。

然而,这两种算法并不是万能的,它们在某些情况下可能会出现不
稳定的情况,甚至会因为一些特殊情况导致死循环。

因此,在实际应用场景中,我们需要针对具体问题的特点来选
择合适的求解最短路的算法。

例如,在需要处理负权边的情况下,Dijkstra算法已经不再适用,因为它只适用于处理非负权边的图。

此时,可以使用Bellman-Ford算法来解决该问题。

还有一种针对稀疏图的优化算法——堆优
化的Dijkstra算法,其可以在处理大型稀疏图时显著提升效率。

除此之外,还有一种通过预处理将最短路问题转化为最小生成
树问题来解决的算法——Floyd算法。

该算法可以处理带有负权环
的图,但是其时间复杂度相对较高。

综上所述,对于不同的最短路问题,我们需要使用不同的求解
算法,并且还需要根据具体问题的特点进行修改和优化。

因此,对
于算法的掌握不仅需要理解其原理,还需要灵活运用和创新。

最短路问题(整理版)

最短路问题(整理版)

最短路问题(short-path problem)若网络中的每条边都有一个权值值(长度、成本、时间等),则找出两节点(通常是源节点与结束点)之间总权和最小的路径就是最短路问题。

最短路问题是网络理论解决的典型问题之一,可用来解决管路铺设、线路安装、厂区布局和设备更新等实际问题。

最短路问题,我们通常归属为三类:单源最短路径问题(确定起点或确定终点的最短路径问题)、确定起点终点的最短路径问题(两节点之间的最短路径)1、Dijkstra算法:用邻接矩阵a表示带权有向图,d为从v0出发到图上其余各顶点可能达到的最短路径长度值,以v0为起点做一次dijkstra,便可以求出从结点v0到其他结点的最短路径长度代码:procedure dijkstra(v0:longint);//v0为起点做一次dijkstrabegin//a数组是邻接矩阵,a[i,j]表示i到j的距离,无边就为maxlongintfor i:=1 to n do d[i]:=a[v0,i];//初始化d数组(用于记录从v0到结点i的最短路径), fillchar(visit,sizeof(visit),false);//每个结点都未被连接到路径里visit[v0]:=true;//已经连接v0结点for i:=1 to n-1 do//剩下n-1个节点未加入路径里;beginmin:=maxlongint;//初始化minfor j:=1 to n do//找从v0开始到目前为止,哪个结点作为下一个连接起点(*可优化) if (not visit[j]) and (min>d[j]) then//结点k要未被连接进去且最小begin min:=d[j];k:=j;end;visit[k]:=true;//连接进去for j:=1 to n do//刷新数组d,通过k来更新到达未连接进去的节点最小值,if (not visit[j]) and (d[j]>d[k]+a[k,j]) then d[j]:=a[k,j]+d[k];end;writeln(d[n]);//结点v0到结点n的最短路。

【转】彻底弄懂最短路径问题(图论)

【转】彻底弄懂最短路径问题(图论)

【转】彻底弄懂最短路径问题(图论)P.S.根据个⼈需要,我删改了不少问题引⼊问题:从某顶点出发,沿图的边到达另⼀顶点所经过的路径中,各边上权值之和最⼩的⼀条路径——最短路径。

解决最短路的问题有以下算法,Dijkstra算法,Bellman-Ford算法,Floyd算法和SPFA算法,另外还有著名的启发式搜索算法A*,不过A*准备单独出⼀篇,其中Floyd算法可以求解任意两点间的最短路径的长度。

笔者认为任意⼀个最短路算法都是基于这样⼀个事实:从任意节点A到任意节点B的最短路径不外乎2种可能,1是直接从A到B,2是从A经过若⼲个节点到B。

⼀.Dijkstra算法该算法在《数据结构》课本⾥是以贪⼼的形式讲解的,不过在《运筹学》教材⾥被编排在动态规划章节,建议读者两篇都看看。

(1) 迪杰斯特拉(Dijkstra)算法按路径长度递增次序产⽣最短路径。

先把V分成两组:S:已求出最短路径的顶点的集合V-S=T:尚未确定最短路径的顶点集合将T中顶点按最短路径递增的次序加⼊到S中,依据:可以证明V0到T中顶点Vk的最短路径,或是从V0到Vk的直接路径的权值或是从V0经S中顶点到Vk的路径权值之和(反证法可证)。

(2) 求最短路径步骤1. 初使时令 S={V0},T={其余顶点},T中顶点对应的距离值,若存在<V0,Vi>,为<V0,Vi>弧上的权值(和SPFA初始化⽅式不同),若不存在<V0,Vi>,为Inf。

2. 从T中选取⼀个其距离值为最⼩的顶点W(贪⼼体现在此处),加⼊S(注意不是直接从S集合中选取,理解这个对于理解vis数组的作⽤⾄关重要),对T中顶点的距离值进⾏修改:若加进W作中间顶点,从V0到Vi的距离值⽐不加W的路径要短,则修改此距离值(上⾯两个并列for循环,使⽤最⼩点更新)。

3. 重复上述步骤,直到S中包含所有顶点,即S=V为⽌(说明最外层是除起点外的遍历)。

NOIP信息学竞赛初赛-图论算法基础-专题十一-1

专题十一:图论算法基础对于图论算法,NOIP初赛不要求会实现算法,但手工操作还是要会的,复赛是要求会代码实现的。

什么是图一个图是一个序偶<V, E>,记为G =<V, E> 。

V 为顶点集, E 为V 中结点之间的边的集合。

自环:一条边的两个端点是相同的。

重边:两个端点之间有两条以上的边,称他们是重边。

简单图:没有自环和重边的图。

无向边:边是双向的。

有向边:单向边,有箭头。

无向图:只有无向边的图。

有向图:只有有向边的图。

混合图:既有无向边又有有向边。

顶点的度:无向图中,一个顶点相连的边数称为该顶点的度;有向图中,从一个顶点出发的边数称为该顶点得出度;到达该顶点的边数称为它的入度。

图论基本定理:著名的握手定理。

无向图中结点度数的总和等于边数的两倍。

有向图中结点入度的和等于出度的和等于边数。

通路:给定图G中结点和边交替出现的一个序列:v0 e1 v1 e2 v2 …ek vk,若每条边ei的两端点是vi-1 和vi ,那么称该序列是从v0到vk的一条通路。

基本通路(路径):没有重复出现的结点的通路。

图的连通性:若一张无向图的任意两个结点之间都存在通路,那么称该图是连通的。

连通分量:图中连通的顶点与边的集合。

权和网:在图的边给出相关的数,成为权。

权可以表示一个顶点到另一个顶点的距离,耗费等。

带权图一般成为网。

最短路径:对于一张不带权的无向图来说,从s到t的最短路径就是所有从s到t的通路中长度最短的那一条(可能不唯一),通路上的边数称为路径的长度。

完全图:任何两个顶点之间都有边(弧)相连称为完全图。

稀疏图、稠密图:边(弧)很少的图称为稀疏图,反之为稠密图。

图的存储:邻接矩阵在邻接矩阵表示中,除了存放顶点本身信息外,还用一个矩阵表示各个顶点之间的关系。

若(i,j)∈E(G)或〈i,j〉∈E(G),则矩阵中第i行第j列元素值为1,否则为0 。

例如, 下面为两个无向图和有向图对应的邻接矩阵。

最短路模板——SPFA(注意事项和易错点)

最短路模板——SPFA(注意事项和易错点)关于SPFASPFA⼀直以来给我的感觉就是⼀个⽞学算法,尤其是时间复杂度,运⽓好是O(km),运⽓差是O(nm)。

所以⽐赛的时候⼀定要使⽤最稳妥的算法——floyd或dijkstra。

以下是标准代码:#include<iostream>#include<cstdio>#include<cmath>#include<cstdlib>#include<cstring>#include<string>#include<algorithm>#include<queue>#include<stack>#include<vector>#include<iomanip>#define MAX 999999999999999#define N 10004#define M 500005using namespace std;long long n,m,st,tot,head[N],edge[M],go[M],nxt[M],d[N];bool v[N];queue<long long> q;long long read(){long long x=0,h=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+(long long)(ch-'0');ch=getchar();}return x*h;}void add(long long x,long long y,long long z){go[++tot]=y;edge[tot]=z;nxt[tot]=head[x];head[x]=tot;return ;}void spfa(){memset(v,0,sizeof(v));for(long long i=0;i<=n+2;i++)d[i]=MAX;d[st]=0;v[st]=1;q.push(st);while(!q.empty()){long long x=q.front(); q.pop();v[x]=0;for(long long i=head[x];i;i=nxt[i]){long long y=go[i];if(d[x]+edge[i]<d[y]){d[y]=d[x]+edge[i];if(!v[y]){v[y]=1;q.push(y);}}}}return ;}int main(){n=read();m=read();st=read();for(long long i=1;i<=m;i++){long long x=read(),y=read(),z=read();add(x,y,z);}spfa();for(long long i=1;i<=n;i++){if(d[i]==MAX)printf("2147483647 ");else printf("%lld ",d[i]);}return 0;}对此代码来说,需要注意以下⼏点:1. 取出队头后v[x]=0;必须加,不能忘记2. if(!v[y])必须写在可以更新此点的if内此代码以洛⾕为模板。

图论论文

最短路算法的比较与应用[摘要]本文较详尽地介绍了最短路算法相关的基本概念,给出了Dijkstra 算法、Floyd 算法、SPFA 算法等常用算法及其核心思想,并对各种最短算法做了多角度的比较,阐述了各种算法的应用范围,并对其在运输网络、舰船通道路线设计、工业生产中的应用做出了举例说明,侧重于模型的建立、思考和证明的过程,最后作出总结。

关键词:最短路算法 Dijkstra 算法 Floyd 算法 SPFA 算法1 引言最短路算法是图论中的核心问题之一,他是许多更深层次算法的基础,同时,该问题有着大量的生产实际的背景.很多问题从表面上看与最短问题没有什么关系,却也可以归结为最短路问题,本文通过收集整理关于最短路径的普遍算法,为研究最短路径问题在一些出行问题,工程问题,及实际生活问题中的应用,为企业和个人提供方便的选择方法。

2 最短路2.1 最短路的定义对最短路问题的研究早在上个世纪60年代以前就卓有成效了,其中对赋权)0(≥ij w 的有效算法是由荷兰著名计算机专家E.W.Dijkstra 在1959年首次提出的,该算法能够解决两指定点间的最短路,也可以求解图G 中一特定点到其它各顶点的最短路。

后来海斯在Dijkstra 算法的基础之上提出了海斯算法.但这两种算法都不能解决含有负权的图的最短路问题.因此由Ford 提出了Ford 算法,它能有效地解决含有负权的最短路问题.但在现实生活中,我们所遇到的问题大都不含负权,所以我们在)0(≥ij w 的情况下选择Dijkstra 算法。

定义1 若图),(E V G G =中各边e 都赋有一个实数)(e W ,称为边e 的权,则称这种图为赋权图,记为),,(W E V G G =。

定义2 若图),(E V G G =是赋权图且)(,0)(G E e e W ∈≥,若u 是i v 到jv 的路)(u W 的权,则称)(u W 为u 的长,长最小的i V 到j V 的路)(u W 称为最短路.若要找出从i v 到n v 的通路u ,使全长最短,即()()min ij e uW u W e ∈=∑。

1求含有负权边的图的单源最短路径——BellmanFord算法与SPFA算法综合分析(上)

1求含有负权边的图的单源最短路径——BellmanFord算法与SPFA算法综合分析(上)求含有负权边的图的单源最短路径——Bellman Ford算法与SPFA算法综合分析对于不含负权边的图求单源最短路径,Dijkstra算法是最⾼效的。

但是在含负权边的图中,Dijkstra很可能得不到正确的结果,因为Dijkstra每次选的是当前能连到的边中权值最⼩的,在正权图中这种贪⼼是对的,但是在负权图中就不是这样了。

⽐如1——>2权值为5,1——>3权值为6,3——>2权值为-2,求1到2的最短路径时,Dijkstra就会选择权为5的1——>2,但实际上1——>3——>2才是最优的结果。

Bellman-Ford与SPFA都是解决这⼀问题的算法,两者的程序都很简单清晰【其实后者就是前者的⼀个优化】,所以也⽐较容易掌握⼀、Bellman-Ford算法说到Bellman-Ford⼀定会说到松弛(relaxation)技术,即⽤d[i]来预计s到i的最短距离。

在计算过程中不断地缩⼩d[i],直到最后d[i]就是源点s到i的最短距离。

⾄于为什么叫“松弛”,尽管算法导论中给出了⼀段说明,但我们只需认为这是作者在故弄⽞虚即可,松弛就是求单源最短路径的迭代过程,d[i]也可以看为存储当前s到i的最短距离。

算法核⼼代码如下:(针对有向图,Pascal语⾔)阅读上述程序,求解下图各点到点1的最短距离:各点距离:0 2 5 9 11 14棕⾊部分:n为图顶点数,m为图边数。

Cost[i,j]表⽰边(i,j)的权Edge存放图的所有边D即上⽂所述的⽤于存储当前s到i的最短距离的数组Closest[i]记录s到i的最短路径上i的前趋。

最后要求i到s的路径时,只需⽤递归打印Closest[i]、Closest[Closest[i]]、Closest[Closest[Closest[i]]、…、s便可求出整条路径。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

/* * 单源最短路算法 SPFA,时间复杂度 O(kE),k 在一般情况下不大于 2,对于每个顶点使用可以在 O(VE)的时间内算出每对节 点之间的最短路 * 使用了队列,对于任意在队列中的点连着的点进行松弛,同时将不在队列中的连着的点入队,直到队空则算法结束,最短路 求出 * SPFA 是 Bellman-Ford 的优化版,可以处理有负权边的情况 * 对于负环,我们可以证明每个点入队次数不会超过 V,所以我们可以记录每个点的入队次数,如果超过 V 则表示其出现负 环,算法结束 * 由于要对点的每一条边进行枚举,故采用邻接表时时间复杂度为 O(kE),采用矩阵时时间复杂度为 O(kV^2) */ #include<cstdio> #include<vector> #include<queue> #define MAXV 10000 #define INF 1000000000 //此处建议不要过大或过小,过大易导致运算时溢出,过小可能会被判定为真正的距离
using std::vector; using std::queue;
struct Edge{ int v; //边权 int to; //连接的点 };
vector<Edge> e[MAXV]; //由于一般情况下 E<<V*V,故在此选用了 vector 动态数组存储,也可以使用链表存储 int dist[MAXV]; //存储到原点 0 的距离,可以开二维数组存储每对节点之间的距离 int cnt[MAXV]; //记录入队次数,超过 V 则退出 queue<int> buff; //队列,用于存储在 SPFA 算法中的需要松弛的节点 bool done[MAXV]; //用于判断该节点是否已经在队列中 int V; //节点数 int E; //边数
bool spfa(const int st){ //返回值:TRUE 为找到最短路返回,FALSE 表示出现负环退出 for(int i=0;i<V;i++){ //初始化:将除了原点 st 的距离外的所有点到 st 的距离均赋上一个极大值 if(i==st){ dist[st]=0; //原点距离为 0; continue; } dist[i]=INF; //非原点距离无穷大 } buff.push(st); //原点入队 done[st]=1; //标记原点已经入队 cnt[st]=1; //修改入队次数为 1 while(!buff.empty()){ //队列非空,需要继续松弛 int tmp=buff.front(); //取出队首元素 for(int i=0;i<(int)e[tmp].size();i++){ //枚举该边连接的每一条边


Edge *t=&e[tmp][i]; //由于 vector 的寻址速度较慢,故在此进行一次优化 if(dist[tmp]+(*t).v<dist[(*t).to]){ //更改后距离更短,进行松弛操作 dist[(*t).to]=dist[tmp]+(*t).v; //更改边权值 if(!done[(*t).to]){ //没有入队,则将其入队 buff.push((*t).to); //将节点压入队列 done[(*t).to]=1; //标记节点已经入队 cnt[(*t).to]=1; //节点入队次数自增 if(cnt[(*t).to]>V){ //已经超过 V 次,出现负环 while(!buff.empty())buff.pop(); //清空队列,释 放内存 return false; //返回 FALSE } } } } buff.pop();//弹出队首节点 done[tmp]=0;//将队首节点标记为未入队 } return true; //返回 TRUE } //算法结束
int main(){ //主函数 scanf("%d%d",&V,&E); //读入点数和边数 for(int i=0,x,y,l;i<E;i++){ scanf("%d%d%d",&x,&y,&l); //读入 x,y,l 表示从 x->y 有一条有向边长度为 l Edge tmp; //设置一个临时变量,以便存入 vector tmp.v=l; //设置边权 tmp.to=y; //设置连接节点 e[x].push_back(tmp); //将这条边压入 x 的表中 } if(!spfa(0)){ //出现负环 printf("出现负环,最短路不存在\n"); \ }else{ //存在最短路 printf("节点 0 到节点%d 的最短距离为%d",V-1,dist[V-1]); } return 0; }

















相关文档
最新文档