• SPFA算法详解


    什么是SPFA算法

    SPFA 算法是Bellman-ford算法队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环

    前置知识:Bellman-ford算法详解_真的没事鸭的博客-CSDN博客

    SPFA算法思路

    SPFA算法其实就是在Bellman-ford算法基础上的优化。Bellman-ford算法看起来比较傻,每次迭代的话是遍历所有边来更新,但是每次迭代的话不是每条边都会更新。SPFA算法就是对这个做优化,每次迭代dist[b]可以更新的话,一定是dist[a]变小了,因为如果dist[a]不变的话,dist[b]一定不变。只有dist[a]变小了,它的后继才会变小。所以SPFA算法就从这个点进行优化。

    SPFA算法的思路就是迭代的时候用一个队列来做,队列里面存的就是到起点距离变小的点。先把起点放到队列里面去,只要队列不空,也就是队列里面还有距离变小的点的话,就执行一下操作:

    1. 先取出队头t,然后队头出队
    2. 更新t的所有出边,t到起点的距离不是变小了吗, 那么所有和t相连的点都有可能变小,如果更新成功的话,就入队。但是注意要判断一下这个点已经入过队的话就不用重复加入了。

    补充

    SPFA算法的时间复杂度一般是O(m),最坏情况下时O(nm)。一般正权图我们是用Dijkstra算法去做,但是其实大部分的正权图的问题都可以用SPFA算法来做。不过出题人卡复杂度的话可能就用不了。所以SPFA算法是由Bellman-ford算法优化来的,不过长得特别像Dijkstra算法。

    案例

    给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数

    请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 impossible

    数据保证不存在负权回路。

    输入格式

    第一行包含整数 n 和 m。

    接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

    输出格式

    输出一个整数,表示 1 号点到 n 号点的最短距离。

    如果路径不存在,则输出 impossible

    数据范围

    1≤n,m≤1e5
    图中涉及边长绝对值均不超过 10000。

    输入样例:

    1. 3 3
    2. 1 2 5
    3. 2 3 -3
    4. 1 3 4

    输出样例:

    2

    来源:ACWing

     思路

    这个和Dijkstra堆优化版的代码相似,就是用队列优化了一下。

    代码实现

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. const int N=1e6+10;
    7. int h[N],e[N],ne[N],idx,w[N];//邻接表,w数组表示权重,idx是两个点之间的桥梁
    8. int n,m;
    9. int dist[N];//每个点距离起点的距离
    10. bool st[N];//判断这个点是否入队
    11. //邻接表模板
    12. void add(int a,int b,int c)
    13. {
    14. e[idx]=b;
    15. w[idx]=c;
    16. ne[idx]=h[a];
    17. h[a]=idx++;
    18. }
    19. int spfa()
    20. {
    21. //初始化距离,1号点距离为0
    22. memset(dist,0x3f,sizeof dist);
    23. dist[1]=0;
    24. //初始一个队列,用来存距离变小的点
    25. queue<int>q;
    26. q.push(1);//起点入队
    27. st[1]=true;
    28. while(q.size())//队列不空
    29. {
    30. int t=q.front();//取出队头
    31. q.pop();//队头出队
    32. st[t]=false;
    33. //遍历它的所有出边,如果能更新的话就更新并且入队
    34. for(int i=h[t];i!=-1;i=ne[i])
    35. {
    36. int j=e[i];//取得出点
    37. if(dist[j]>dist[t]+w[i])//如果可以更新的话
    38. {
    39. dist[j]=dist[t]+w[i];//更新这个点距离起点的距离
    40. if(!st[j])//判断是否在队中,不在的话入队
    41. {
    42. st[j]=true;
    43. q.push(j);
    44. }
    45. }
    46. }
    47. }
    48. return dist[n];
    49. }
    50. int main()
    51. {
    52. cin>>n>>m;
    53. //初始化h数组
    54. memset(h,-1,sizeof h);
    55. while(m--)
    56. {
    57. int a,b,c;
    58. cin>>a>>b>>c;
    59. //这里不考虑重边
    60. add(a,b,c);
    61. }
    62. if(spfa()==0x3f3f3f3f)
    63. cout<<"impossible";
    64. else
    65. cout<<spfa();
    66. return 0;
    67. }
  • 相关阅读:
    第一课数组、链表、栈、队列
    聊聊简单又不简单的图上多跳过滤查询
    MySQL 存储函数及调用
    vmware设置桥接模式后ip设置
    vs code实现XML代码补全
    Spring Boot 2.x系列【13】功能篇之声明Bean的八种方式
    Scala爬虫实战:采集网易云音乐热门歌单数据
    Linux学习第39天:Linux I2C 驱动实验(三):哥俩好
    UNIX环境高级编程-第六章-系统数据文件和信息
    在macOS上使用dosbox-x配置masm汇编环境
  • 原文地址:https://blog.csdn.net/qq_52905520/article/details/126574584