[关闭]
@Yeasion-Nein 2018-04-10T21:50:38.000000Z 字数 3057 阅读 733

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. queueq,也就是队列。
7. flag[MAXN]:f[i]表示i在不在队列中

首先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. }

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

Created with Raphaël 2.1.2现在我们建图, 里面包含有a b c d e f gaabbddccffeegg2415837269335

看不看得懂呢?~ ~ ~

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

Created with Raphaël 2.1.2aabbccddeeffgg0

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

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

此时表格的状态为:

Created with Raphaël 2.1.2aabbccddeeffgg024815

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

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

此时表格的状态变为:

Created with Raphaël 2.1.2aabbccddeeffgg02481530

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

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

此时表格又变了样子:

Created with Raphaël 2.1.2aabbccddeeffgg0248151511

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

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

表格再次变化:

Created with Raphaël 2.1.2aabbccddeeffgg024815151119

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

Created with Raphaël 2.1.2aabbccddeeffgg017815131114

此时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)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注