当前位置:   article > 正文

洛谷[P4779]单源最短路径(标准版)_洛谷p4779

洛谷p4779

前言

  • SPFASPFA算法由于它上限 O(NM) = O(VE)O(NM)=O(VE)的时间复杂度,被卡掉的几率很大.在算法竞赛中,我们需要一个更稳定的算法:dijkstradijkstra.

什么是dijkstradijkstra?

  • dijkstradijkstra是一种单源最短路径算法,时间复杂度上限为O(n^2)O(n2)(朴素),在实际应用中较为稳定;;加上堆优化之后更是具有O((n+m)\log_{2}n)O((n+m)log2​n)的时间复杂度,在稠密图中有不俗的表现.

dijkstradijkstra的原理/流程?

  • dijkstradijkstra本质上的思想是贪心,它只适用于不含负权边的图.
  • 我们把点分成两类,一类是已经确定最短路径的点,称为"白点",另一类是未确定最短路径的点,称为"蓝点"
  • dijkstradijkstra的流程如下::
  • 1.1. 初始化dis[start] = 0,dis[start]=0,其余节点的disdis值为无穷大.
  • 2.2. 找一个disdis值最小的蓝点x,x,把节点xx变成白点.
  • 3.3. 遍历xx的所有出边(x,y,z),(x,y,z),若dis[y] > dis[x] + z,dis[y]>dis[x]+z,则令dis[y] = dis[x] + zdis[y]=dis[x]+z
  • 4.4. 重复2,32,3两步,直到所有点都成为白点..
  • 时间复杂度为O(n^2)O(n2)

dijkstradijkstra为什么是正确的

  • 当所有边长都是非负数的时候,全局最小值不可能再被其他节点更新.所以在第22步中找出的蓝点xx必然满足:dis[x]:dis[x]已经是起点到xx的最短路径..我们不断选择全局最小值进行标记和拓展,最终可以得到起点到每个节点的最短路径的长度

图解

  • (令start = 1start=1)
  • 开始时我们把dis[start]dis[start]初始化为00,其余点初始化为infinf 

  • 第一轮循环找到disdis值最小的点11,将11变成白点,对所有与11相连的蓝点的disdis值进行修改,使得dis[2]=2,dis[3]=4,dis[4]=7dis[2]=2,dis[3]=4,dis[4]=7 

  • 第二轮循环找到disdis值最小的点22,将22变成白点,对所有与22相连的蓝点的disdis值进行修改,使得dis[3]=3,dis[5]=4dis[3]=3,dis[5]=4 

  • 第三轮循环找到disdis值最小的点33,将33变成白点,对所有与22相连的蓝点的disdis值进行修改,使得dis[4]=4dis[4]=4 

  • 接下来两轮循环分别将4,54,5设为白点,算法结束,求出所有点的最短路径
  • 时间复杂度O(n^2)O(n2)

为什么dijkstradijkstra不能处理有负权边的情况?

  • 我们来看下面这张图 

  • 22到33的边权为-4−4,显然从11到33的最短路径为-2−2 (1->2->3).(1−>2−>3).但在循环开始时程序会找到当前disdis值最小的点33,并标记它为白点.
  • 这时的dis[3]=1,dis[3]=1,然而11并不是起点到33的最短路径.因为33已经被标为白点,所以dis[3]dis[3]不会再被修改了.我们在边权存在负数的情况下得到了错误的答案.

dijkstradijkstra的堆优化?

  • 观察dijkstradijkstra的流程,发现步骤22可以优化

  • 怎么优化呢?

  • 我会zkw线段树!我会斐波那契堆!

  • 我会堆!

  • 我们可以用堆对disdis数组进行维护,用O(\log_{2}n)O(log2​n)的时间取出堆顶元素并删除,用O(\log_{2}n)O(log2​n)遍历每条边,总复杂度O((n+m)\log_{2}n)O((n+m)log2​n)

  • 范例代码:

    1. #include<bits/stdc++.h>
    2. const int MaxN = 100010, MaxM = 500010;
    3. struct edge
    4. {
    5. int to, dis, next;
    6. };
    7. edge e[MaxM];
    8. int head[MaxN], dis[MaxN], cnt;
    9. bool vis[MaxN];
    10. int n, m, s;
    11. inline void add_edge( int u, int v, int d )
    12. {
    13. cnt++;
    14. e[cnt].dis = d;
    15. e[cnt].to = v;
    16. e[cnt].next = head[u];
    17. head[u] = cnt;
    18. }
    19. struct node
    20. {
    21. int dis;
    22. int pos;
    23. bool operator <( const node &x )const
    24. {
    25. return x.dis < dis;
    26. }
    27. };
    28. std::priority_queue<node> q;
    29. inline void dijkstra()
    30. {
    31. dis[s] = 0;
    32. q.push( ( node ){0, s} );
    33. while( !q.empty() )
    34. {
    35. node tmp = q.top();
    36. q.pop();
    37. int x = tmp.pos, d = tmp.dis;
    38. if( vis[x] )
    39. continue;
    40. vis[x] = 1;
    41. for( int i = head[x]; i; i = e[i].next )
    42. {
    43. int y = e[i].to;
    44. if( dis[y] > dis[x] + e[i].dis )
    45. {
    46. dis[y] = dis[x] + e[i].dis;
    47. if( !vis[y] )
    48. {
    49. q.push( ( node ){dis[y], y} );
    50. }
    51. }
    52. }
    53. }
    54. }
    55. int main()
    56. {
    57. scanf( "%d%d%d", &n, &m, &s );
    58. for(int i = 1; i <= n; ++i)dis[i] = 0x7fffffff;
    59. for( register int i = 0; i < m; ++i )
    60. {
    61. register int u, v, d;
    62. scanf( "%d%d%d", &u, &v, &d );
    63. add_edge( u, v, d );
    64. }
    65. dijkstra();
    66. for( int i = 1; i <= n; i++ )
    67. printf( "%d ", dis[i] );
    68. return 0;
    69. }

例题

  • 入门模板:P3371
  • 进阶模板:P4779
  • 其余例题请右转洛谷题库,搜索"最短路"

后记

  • 本文部分内容摘自李煜东《算法竞赛进阶指南》和《信息学竞赛一本通》
  • 友情提示:正权图请使用dijkstradijkstra算法,负权图请使用SPFASPFA算法
  • 感谢洛谷各位管理员提供的平台

下一章讲弱化版,也就是洛谷【P3371】单源最短路径(弱化版)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/622364
推荐阅读
相关标签
  

闽ICP备14008679号