SPFA全面讲解

——最短路高效算法

标签: 最短路

简介:SPFA 是1994年在西安交通大学段凡丁同学所提出,是将Dijsktra以及Bellman-Ford两种最短路算法完美结合的一个算法,效率十分的高。全名为Shortest Path Faster Algorithm,简称SPFA。

首先,在下面的讲解中,我们要用到几个变量:

  1. n 表示一共有n个点。
  2. s 表示开始点。
  3. t 表示结束点。
  4. dist[MAXN]:d[i]表示从s到i的最短路径
  5. head[MAXN]:head[i]记录前驱。
  6. queue\(<int>\)q,也就是队列。
  7. flag[MAXN]:f[i]表示i在不在队列中

\(SPFA可以处理负权边!!\)

首先add一个邻接表以及一个用来搞邻接表的struct

  1. struct point
  2. {
  3. int from;
  4. int to;
  5. int next;
  6. int len;
  7. }edge[MAXN];
  8. int total=0;
  9. void add(int f,int t,int l)
  10. {
  11. edge[total++].from=f;
  12. edge[total].to=t;
  13. edge[total].next=head[f];
  14. edge[total].len=l;
  15. head[f]=total;
  16. }

首先,我们先处理初始化,顺带输入。。。

  1. int main()
  2. {
  3. scanf("%d%d",&n,&m);
  4. scanf("%d%d",&s,&t);
  5. for(int i=1;i<=n;i++)
  6. dist[i]=INF;
  7. //预处理操作
  8. {
  9. dist[s]=0;//源点到源点的距离为0
  10. q.push(s);//将源点入队
  11. flag[s]=1;//表示s点已经在队列中
  12. }
  13. SPFA(s);
  14. }

然后就是队列+松弛操作。

  1. int SPFA()
  2. {
  3. while(!q.empty())
  4. {
  5. int cnt=q.front();
  6. q.pop();
  7. flag[cnt]=0;
  8. for(int i=head[cnt];i;i=edge[i].next)
  9. if(dist[edge[i].to]>dist[cnt]*edge[i].len)
  10. {
  11. dist[edge[i].to]=dist[cnt]*edge[i].len;
  12. if(!flag[edge[i].to])
  13. {
  14. flag[flag[edge[i].to]]=1;
  15. q.push(edge[i].to);
  16. }
  17. }
  18. }
  19. }

那么正式的讲解从现在开始!!
首先我们建一个图用来方便讲解。

  1. Title:现在我们建图, 里面包含有a b c d e f g
  2. a->b: 24
  3. a->d:15
  4. a->c:8
  5. c->f:3
  6. c->e:7
  7. f->e:2
  8. b->e:6
  9. e->g:9
  10. g->b:3
  11. f->g:3
  12. f->d:5

看不看得懂呢?~ ~ ~

然后我们假设a为s。
那么我们现在建立一个从起始点a到个点的最短路径表格。

  1. a->a:0
  2. a->b:&infin;
  3. a->c:&infin;
  4. a->d:&infin;
  5. a->e:&infin;
  6. a->f:&infin;
  7. a->g:&infin;

然后按照我们的SPFA的顺序,首先a入队,然后判断到队列非空。
将队首元素a出队.

然后对以a点为起始点的所有边进行松弛操作(此处只有e点。)。

此时表格的状态为:

  1. a->a:0
  2. a->b:24
  3. a->c:8
  4. a->d:15
  5. a->e:&infin;
  6. a->f:&infin;
  7. a->g:&infin;

在松弛的时候三个点的最短路径(估值)变小了,然后检测到这些点在队列中还都没有出现。于是入队,此时队列中有了三个点:b,c,d。
然后队首元素c出队.

对以c为起始点的所有边进行松弛操作。

此时表格的状态变为:

  1. a->a:0
  2. a->b:24
  3. a->c:8
  4. a->d:15
  5. a->e:30
  6. a->f:&infin;
  7. a->g:&infin;

此时在列表中e的路径估值也变小了,而且e不在队列之中,于是e也入队,于是队列中的元素变成了c,d,e。
然后队首元素c再次出队.

对所有以c为起始点的边进行松弛操作。

此时表格又变了样子:

  1. a->a:0
  2. a->b:24
  3. a->c:8
  4. a->d:15
  5. a->e:15
  6. a->f:11
  7. a->g:&infin;

看到了e和f的最短路径估值再次变小,但是e在队列中但是f不在,于是将f入队。
队首元素d出队

对以d为起始点的所有边进行松弛操作。

表格再次变化:

  1. a->a:0
  2. a->b:24
  3. a->c:8
  4. a->d:15
  5. a->e:15
  6. a->f:11
  7. a->g:19

此时g的最短路径估值没有变小,于是松弛失败,没有新节点入队。于是接着取队首,f,g......
最后我们的表格变成了这个样子:

  1. a->a:0
  2. a->b:17
  3. a->c:8
  4. a->d:15
  5. a->e:13
  6. a->f:11
  7. a->g:14

此时e的最短路径估值没有变化,于是松弛失败,此时队列为空,于是程序结束。
然后我们要求的dist[g]就是14。

\(_完美收工_\) \(_完美收工_\) \(_完美收工_\) \(_完美收工_\) \(_完美收工_\) \(_完美收工_\) \(_完美收工_\)

那么下面给大家出一道SPFA的模板题,(用来存代码(#滑稽)
若要看具体题面请看链接:传送门

##题目描述:最短路

给定n个带权的有向图,,求1到n的最短的简单路径之积。

输入:

一共m+1行。
第一行:两个数n,m.分别表示点的总数以及边的总数。
第2到第m+1行:每一行三个数:分别为两个点以及连接这两个点的边权。

输出:

一行,共一个数:表示所求路径的边权之积mod 9987的值。

输入样例:

3 3
1 2 3
2 3 3
1 3 10

输出样例:

9

很明显的模板题了。下面是代码:

  1. //Yeasion_nein
  2. #include<iostream>
  3. #include<cstdio>
  4. #include<cstring>
  5. #include<string>
  6. #include<algorithm>
  7. #include<queue>
  8. #define MAXN 10010
  9. using namespace std;
  10. int n,m,head[1000010];
  11. int dist[1000010];
  12. bool flag[1000010];
  13. queue<int>q;
  14. int total;
  15. struct e
  16. {
  17. int next;
  18. int to;
  19. int from;
  20. int len;
  21. }edge[1000010];
  22. void add(int f,int t,int l)
  23. {
  24. edge[total++].from=f;
  25. edge[total].to=t;
  26. edge[total].next=head[f];
  27. edge[total].len=l;
  28. head[f]=total;
  29. }
  30. int SPFA()
  31. {
  32. while(!q.empty())
  33. {
  34. int cnt=q.front();
  35. q.pop();
  36. flag[cnt]=0;
  37. for(int i=head[cnt];i;i=edge[i].next)
  38. {
  39. if(dist[edge[i].to]>dist[cnt]*edge[i].len)
  40. {
  41. dist[edge[i].to]=dist[cnt]*edge[i].len;
  42. if(!flag[edge[i].to])
  43. {
  44. flag[flag[edge[i].to]]=1;
  45. q.push(edge[i].to);
  46. }
  47. }
  48. }
  49. }
  50. }
  51. int main()
  52. {
  53. scanf("%d%d",&n,&m);
  54. for(int i=1;i<=n;i++)
  55. dist[i]=0x7ffffff;
  56. for(int i=1;i<=m;i++)
  57. {
  58. int x,y,z;
  59. scanf("%d",&x);
  60. scanf("%d",&y);
  61. scanf("%d",&z);
  62. add(x,y,z);
  63. }
  64. dist[1]=1;
  65. q.push(1);
  66. flag[1]=1;
  67. SPFA();
  68. printf("%d",dist[n]%9987);
  69. return 0;
  70. }

如果这篇博客有帮助到你的话,清点一下赞吧!!(qwq)

SPFA 全面讲解的更多相关文章

  1. spfa模板+讲解

    zz http://blog.sina.com.cn/s/blog_6ad20aef0100mc1a.html Spfa算法 (模板源代码) 这是Bellman Ford的改进算法.    算法介绍: ...

  2. 图论最短路径算法——Dijkstra

    说实在的,这算法很简单,很简单,很简单--因为它是贪心的,而且码量也小,常数比起SPFA也小. 主要思想 先初始化,dis[起点]=0,其它皆为无限大. 还要有一个bz数组,bz[i]表示i是否确定为 ...

  3. 【算法】祭奠spfa 最短路算法dijspfa

    题目链接 本题解来源 其他链接 卡spfa的数据组 题解堆优化的dijkstra 题解spfa讲解 来自以上题解的图片来自常暗踏阴 使用前向星链表存图 直接用队列优化spfa struct cmp { ...

  4. 觉得一篇讲SPFA还不错的文章

    我觉得他整理的有一些乱,我都改成插入代码了,看的顺眼一些 转载自http://blog.csdn.net/juststeps/article/details/8772755 下面的都是原文: 最短路径 ...

  5. 图论最短路径算法总结(Bellman-Ford + SPFA + DAGSP + Dijkstra + Floyd-Warshall)

    这里感谢百度文库,百度百科,维基百科,还有算法导论的作者以及他的小伙伴们...... 最短路是现实生活中很常见的一个问题,之前练习了很多BFS的题目,BFS可以暴力解决很多最短路的问题,但是他有一定的 ...

  6. POJ3169--Layout(SPFA+差分系统)

    Description Like everyone else, cows like to stand close to their friends when queuing for feed. FJ ...

  7. 关于$NOIP2017$的题目讲解

    关于\(NOIP2017\)的题目讲解 1.小凯的疑惑 题目描述: 小凯手中有两种面值的金币,两种面值均为正整数且彼此互素.每种金币小凯都有 无数个.在不找零的情况下,仅凭这两种金币,有些物品他是无法 ...

  8. 【最短路径】 SPFA算法

    上一期介绍到了SPFA算法,只是一笔带过,这一期让我们详细的介绍一下SPFA. 1 SPFA原理介绍 SPFA算法和dijkstra算法特别像,总感觉自己讲的不行,同学说我的博客很辣鸡,推荐一个视频讲 ...

  9. 算法提高 道路和航路 SPFA 算法

    我简单的描述一下题目,题目中所说的有道路和航路: 1.公路是双向的,航路是单向的: 2.公路是正值,航路可正可负: 每一条公路i或者航路i表示成连接城镇Ai(1<=A_i<=T)和Bi(1 ...

随机推荐

  1. windows环境下MySQL-5.7.12-winx64下载安装与配置

    系统:64位Win-7 官网压缩包:mysql-5.7.12-winx64.zip 前后花了一些时间,以前都是下载软件直接安装在本地,现在这个不一样,下载压缩包后要解压缩到安装目录,然后在控制台下配置 ...

  2. word 摘要

    word 使用心得 定义快捷键 Tools -> Customize keyboard 自定义快捷键 cmd + L, 左对齐; cmd + R, 右对齐; cmd + E, 居中对齐 cmd ...

  3. Linux文本处理工具

    Linux文本处理工具 Linux中熟练的使用文本处理工具非常的重要, 因为Linux在设计的时候是采用一切皆文件的哲学的, 甚至连计算机中的配置也都使用伪文件系统来表示, 要查询里面的内容就是对文件 ...

  4. nyoj 409——郁闷的C小加(三)——————【中缀式化前缀后缀并求值】

    郁闷的C小加(三) 时间限制:1000 ms  |  内存限制:65535 KB 难度:4   描述 聪明的你帮助C小加解决了中缀表达式到后缀表达式的转换(详情请参考“郁闷的C小加(一)”),C小加很 ...

  5. jquery中Ajax提交配合PHP使用的注意事项-编码

    问题:Ajax提交的数据的编码为utf-8,并且返回的数据也要求是utf-8的,如果说你的系统不是utf-8编码的话,那会让你痛不欲生! 解决方法:(比较笨拙的方法,但是很好用) 对于接收的数据,使用 ...

  6. 项目视图 Project Browser

    项目视图 在这个视图,你可以访问.管理你当前项目资源. 项目视图左侧面板显示项目的文件夹结构的分层列表,当从列表中单击一个文件夹,其内容会显示在面板的右边.你可以点击小三角展开或折叠文件夹,显示他包含 ...

  7. Hibernate中的一对一注解配置

    Card类 package cn.OneToOne2017109.entity; import javax.persistence.*; /** * Created by YSS on 2017/10 ...

  8. Cocos2d-js 开发记录:图片数据资源等的异步加载

    这里说的是在需要的使用加载图片,比如游戏中的某个关卡的图片,不用在游戏一开始就加载(万一用户玩不到那关,岂不是很冤,流量费了那么多),否则载入速度也慢.这种方式加载资源要用到cc.loader官方文档 ...

  9. springboot--数据库操作

    1.注意: 使用get,post提交时,使用form-data; 使用put提交方式,使用x-www-form-url-encoded,这是http的一种格式;

  10. foreach的基本语法

    有一个布尔型循环是专门用来循环数组的.这个循环的基本语法就是foreach基本语法 foreach( 要循环的数组变量 as [键变量 =>] 值变量){ //循环的内容 } 这是一个固定用法, ...