【算法】最短路问题超详细
创始人
2025-05-30 04:10:39
0

作者:指针不指南吗
专栏:算法篇

🐾算法理解没有用,会思路,会敲代码才OK🐾

文章目录

  • 0.知识结构图
  • 1.朴素Dijkstra算法
    • 1.1思想
    • 1.2图示
    • 1.3模板题
  • 2.堆优化版的Dijkstra算法
    • 2.1思想
    • 2.2模板题
  • 3.Bellman-Ford算法
    • 3.1思想
    • 3.2模板题
  • 4.SPFA算法
    • 4.1思想
    • 4.2图示
    • 4.2模板题
    • 4.4判断负环
  • 5.Floyd算法
    • 5.1思想
    • 5.2模板题
  • 五种算法总结🎈

0.知识结构图

每种情况下,都有一个最合适的算法

n 表示顶点的个数,m表示边的个数

在这里插入图片描述

  • 单源最短路:1号点到所有点的最短路;多元最短路:任意两个点之间的最短路

  • 稠密图和稀疏图:m是 10510^5105 级别的话就是稠密图,m是n级别的就是稀疏图


  • ⭐️难点

    出题的重难点在于建图 -> 把题目抽象成最短图问题,重点学习算法的实现,原理了解即可(比赛是不会考原理),做到会应用,会做题。无向图没有专门的算法,无向图即两个有向边,这里讲有向图。

  • ⭐️牢记时间复杂度

    注意时间复杂度,看一下数据范围,可以给我们很多提示

  • ⭐️学习

    算法理解没有用,会思路,会敲代码;背模板,压短写算法的时间压短,你的代码水平就会有很高的提升。

  • ⭐️调代码技巧:printf大法(出现错误答案时);删代码(出现段错误时)

1.朴素Dijkstra算法

1.1思想

集合S为已经确定最短路径的点集。

  1. 初始化距离
    一号结点的距离为零,其他结点的距离设为无穷大(看具体的题)。
  2. 循环n次,每一次将集合S之外距离最短X的点加入到S中去(这里的距离最短指的是距离1号点最近。
    点X的路径一定最短)。然后用点X更新X邻接点的距离。
  3. 两层循环,时间复杂度为 O($n^2 $)。
s[] ; //表示当前已确定的最短距离
//1.初始化距离
dis[1]=0,dis[i]=inf;  //dis表示到起点的最短距离,inf表示正无穷
//2.迭代过程,就是用个循环
for(int i=0;idis(t)+w[x]  //用 t 更新其他点的距离 

1.2图示

略微模糊,但是思路清晰

求 a 到 b 的最短路

从编号 1 开始 ,找起点到每个编号的最短路

红点表示已经确定最短路的顶点,

蓝色表示可以走到,但不能确定是最短路

绿色的线表示,更新当前顶点的邻接点的距离

在这里插入图片描述

1.3模板题

  • ⭐️题目

    链接: 849. Dijkstra求最短路 I - AcWing题库

    给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。

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

    输入格式

    第一行包含整数 n 和 m。

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

    输出格式

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

    如果路径不存在,则输出 −1。

    数据范围

    1≤n≤500,
    1≤m≤10510^5105 ,
    图中涉及边长均不超过10000。

    输入样例:

    3 3
    1 2 2
    2 3 1
    1 3 4
    

    输出样例:

    3
    
  • ⭐️题解

    #include
    using namespace std;const int N=510;  
    int g[N][N];  //用一个邻接矩阵来存储稠密图
    int dist[N];  //表示每个点到起点的最短路
    bool st[N];  //true表示已经确定最短路,属于s集合
    int n,m;int dijkstra()  
    {//给距离初始化memset(dist,0x3f,sizeof dist); //每个顶点到起点的距离是无限大dist[1]=0;  //起点到起点的距离是 0//迭代即循环过程for(int i=1;i<=n;i++) //遍历每一个顶点每次可以确定一个点到起点的最短路{int t=-1;  //t 来存储当前访问这个点for(int j=1;j<=n;j++)if(!st[j]&&(t==-1||dist[t]>dist[j]))  //j到起点的最短路还没有确定并且t没有被更新过,或者找到比t 还短的距离t=j;st[t]=true;  //已经确定当前 点 t 的最短路for(int j=1;j<=n;j++)  //利用距离最小的点,去更新其他点到其他点到起点的距离{dist[j]=min(dist[j],dist[t]+g[t][j]);  }}if(dist[n]==0x3f3f3f3f)  return -1;  //如果起点到达不了n号节点,则返回-1return dist[n]; //否则,直接返回最短路的值}int main()
    {scanf("%d%d",&n,&m);memset(g,0x3f,sizeof g);  //初始化,所有的边,无限大while(m--){int a,b,c;scanf("%d%d%d",&a,&b,&c);  //存边,邻接矩阵,因为是稠密图g[a][b]=min(g[a][b],c);  //可能会出现重边的情况,存下最小的边}printf("%d",dijkstra()); return 0;
    }
    

2.堆优化版的Dijkstra算法

2.1思想

  • ⭐️思路

    堆优化版的dijkstra是对朴素版dijkstra进行了优化,在朴素版dijkstra中时间复杂度最高的寻找距离
    最短的点O(10510^5105 )可以使用最小堆优化。

    1. 一号点的距离初始化为零,其他点初始化成无穷大。
    2. 将一号点放入堆中。
    3. 不断循环,直到堆空。每一次循环中执行的操作为:
      弹出堆顶(与朴素版diijkstra找到S外距离最短的点相同,并标记该点的最短路径已经确定)。
      用该点更新临界点的距离,若更新成功就加入到堆中。
  • ⭐️堆的实现

    方式手写堆优先队列(STL)
    区别可以保证n个数,支持修改堆中任意元素,使用映射不支持修改,每次修改需要新加一个数,有m个数,好写方便
  • ⭐️稠密图与稀疏图

    • 连线很多的时候,对应的就是稠密图,显然易见,稠密图的路径太多了,所以就用点来找,也就是抓重点;
    • 点很多,但是连线不是很多的时候,对应的就是稀疏图,稀疏图的路径不多,所以按照连接路径找最短路,这个过程运用优先队列,能确保每一次查找保留到更新到队列里的都是最小的,同时还解决了两个点多条路选择最短路的问题。

2.2模板题

  • ⭐️题目

    链接: 850. Dijkstra求最短路 II - AcWing题库

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

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

    输入格式

    第一行包含整数 n 和 m。

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

    输出格式

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

    如果路径不存在,则输出 −1。

    数据范围

    1≤n,m≤1.5×10510^5105 ,
    图中涉及边长均不小于 0,且不超过 10000。
    数据保证:如果最短路存在,则最短路的长度不超过 10910^9109 。

    输入样例:

    3 3
    1 2 2
    2 3 1
    1 3 4
    

    输出样例:

    3
    
  • ⭐️题解

    #include
    using namespace std;typedef pair PII;const int N=1.5*1e5+10; int n,m;
    int h[N],w[N],ne[N],e[N],idx;  //稀疏图用邻接表来存
    int dist[N];  //表示该点到起点的距离
    bool st[N];   //表示该点的到起点的最短距离有没有被确定void add(int a,int b,int c)  //存边和边权值
    {e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;  //记住这个顺序,不可以变!!
    }int dijkstra()
    {memset(dist,0x3f,sizeof dist);  //初始化各点到起点的最短距离dist[1]=0; priority_queue,greater> heap;  //定义根小堆,内部根据距离排序,保证堆顶是距离最小的顶点;这个顺序不能倒,pair排序时是先根据first,再根据second,heap.push({0,1});  //把起点放进去while(heap.size())  //队列不空{auto t=heap.top();  //取出队列中最短路径最小的点heap.pop();int ver=t.second,distance=t.first; //简写这个 当前的队列元素if(st[ver]) continue;  //当前的元素 最短已经被确定,跳过这个点,找下一个st[ver]=true;  //改变他的状态,表示当前的顶点最短距离已经被确定for(int i=h[ver];i!=-1;i=ne[i]) //遍历 当前顶点的相邻顶点{int j=e[i]; //j 当前相邻顶点的编号,i只是一个下标而不是编号if(dist[j]>distance+w[i])  //更新 相邻顶点到起点的最短距离{dist[j]=distance+w[i];heap.push({dist[j],j});  //距离变小则入队}}}if(dist[n]==0x3f3f3f3f) return -1;  //如果n到起点的距离是正无穷,说明n与起点不通return dist[n]; //否在,返回n到起点的最短路
    }int main()
    {scanf("%d%d",&n,&m);memset(h,-1,sizeof h);  //初始化邻接表while(m--){int a,b,c;scanf("%d%d%d",&a,&b,&c);  //加边add(a,b,c);}printf("%d",dijkstra());return 0;
    }
    

3.Bellman-Ford算法

3.1思想

  • ⭐️为什么 Dijkstra 算法不能解决负权边?

    • dijkstra要求每个点被确定后st[j] = true,dist[j]就是最短距离了,之后就不能再被更新了(一锤子买卖),而如果有负权边的话,那已经确定的点的dist[j]不一定是最短了。

    • dijstra算法基于贪心思想,当有负权边时,局部最优不一定是全局最优

  • ⭐️什么是Bellman-Ford算法?

    • Bellman - ford 算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小。其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在n-1 次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。
    • 通俗的来讲就是:假设 1 号点到 n 号点是可达的,每一个点同时向指向的方向出发,更新相邻的点的最短距离,通过循环 n-1 次操作,若图中不存在负环,则 1 号点一定会到达 n 号点,若图中存在负环,则在 n-1 次松弛后一定还会更新。
  • ⭐️步骤

    for n次 //表示更新n条边,还要记得备注for 所有边 a,b,w (松弛操作)dist[b] = min(dist[b],back[a] + w)//注意:back[] 数组是上一次迭代后 dist[] 数组的备份,由于是每个点同时向外出发,因此需要对 dist[] 数组进行备份,若不进行备份会因此发生串联效应,影响到下一个点
    
  • ⭐️是否能到达n号点的判断中需要进行if(dist[n] > INF/2)判断

    • 在下面代码中,是否能到达n号点的判断中需要进行if(dist[n] > INF/2)判断,而并非是if(dist[n] == INF)判断,原因是INF是一个确定的值,并非真正的无穷大,会随着其他数值而受到影响,dist[n]大于某个与INF相同数量级的数即可

    • 比如5号节点距离起点的距离是无穷大,利用5号节点更新n号节点距离起点的距离,将得到10510^5105 −2,它 虽然小于10910^9109 , 但并不存在最短路,(在边数限制在k条的条件下)。

  • ⭐️只要题中没有负环就可以用这个算法

    有负权回路,是没有最短路的,负权回路的最小路就是负无穷

  • ⭐️但是如果有边数限制的话,求最短路有负权回路也就无所谓了,也只能使用Bellman-Ford算法

    举个例子:比如乘客从某个城市到另一个城市,之间可以进行周转,但是没周转一次,乘客的心情就会变坏一次,所以限制周转的次数为k次,这样就限制了边的次数

3.2模板题

  • ⭐️题目

    链接: 853. 有边数限制的最短路 - AcWing题库

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

    请你求出从 11 号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出 impossible

    注意:图中可能 存在负权回路

    输入格式

    第一行包含三个整数 n,m,k。

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

    点的编号为 1∼n。

    输出格式

    输出一个整数,表示从 1 号点到 n 号点的最多经过 k 条边的最短距离。

    如果不存在满足条件的路径,则输出 impossible

    数据范围

    1≤n,k≤500,
    1≤m≤10000,
    1≤x,y≤n,
    任意边长的绝对值不超过 10000。

    输入样例:

    3 3 1
    1 2 1
    2 3 1
    1 3 3
    

    输出样例:

    3
    
  • ⭐️题解

    #include
    using namespace std;const int N=510,M=10010;int n,m,k;
    int dist[N],backup[N];  //dist表示该点到起点的最短路,backup用来备份,具体看函数内部实现struct Edge{int a,b,w;
    }edges[M];  //a到b的距离为 wint bellman_ford()
    {memset(dist,0x3f,sizeof dist);  //初始化 每个顶点到起点的最短路dist[1]=0;for(int i=0;imemcpy(backup,dist,sizeof dist);  //备份for(int j=0;jint a=edges[j].a,b=edges[j].b,w=edges[j].w;   //松弛,更新边最短路dist[b]=min(dist[b],backup[a]+w);}}if(dist[n]>0x3f3f3f3f/2)  return 0x3f3f3f3f/2+1;  //n到起点的距离是无穷,表示n到起点不通return dist[n];  //否则返回 n到起点的最短路
    }int main()
    {scanf("%d%d%d",&n,&m,&k);for(int i=0;iint a,b,w;scanf("%d%d%d",&a,&b,&w);edges[i]={a,b,w};  //加边}int t=bellman_ford();if(t>0x3f3f3f3f/2) puts("impossible");else printf("%d\n",t);return 0;
    }
    

4.SPFA算法

4.1思想

  • ⭐️分析
    SPFA算法仅仅只是对该Bellman-ford算法的一个优化。

    我觉得吧,区别就是Bellman-Ford没有用邻接表,而SPFA用了邻接表

    Bellman_ford算法会遍历所有的边,但是有很多的边遍历了其实没有什么意义,我们只用遍历那些到源点距离变小的点所连接的边即可,只有当一个点的前驱结点更新了,该节点才会得到更新;因此考虑到这一点,我们将创建一个队列每一次加入距离被更新的结点。

  • ⭐️注意

    1. st数组的作用:判断当前的点是否已经加入到队列当中了;已经加入队列的结点就不需要反复的把该点加入到队列中了,就算此次还是会更新到源点的距离,那只用更新一下数值而不用加入到队列当中。
      即便不使用st数组最终也没有什么关系,但是使用的好处在于可以提升效率。

    2. SPFA算法看上去和 Dijstra 算法长得有一些像但是其中的意义还是相差甚远的:

      1. Dijkstra算法中的st数组保存的是当前确定了到源点距离最小的点,且一旦确定了最小那么就不可逆了(不可标记为true后改变为false);

        SPFA算法中的st数组仅仅只是表示的当前发生过更新的点,且spfa中的st数组可逆(可以在标记为true之后又标记为false)。顺带一提的是BFS中的st数组记录的是当前已经被遍历过的点。

      2. Dijkstra算法里使用的是优先队列保存的是当前未确定最小距离的点,目的是快速的取出当前到源点距离最小的点;

        SPFA算法中使用的是队列(你也可以使用别的数据结构),目的只是记录一下当前发生过更新的点。

    3. Bellman-ford算法里最后return-1的判断条件写的是dist[n]>0x3f3f3f3f/2;而spfa算法写的是dist[n]==0x3f3f3f3f;

      其原因在于Bellman_ford算法会遍历所有的边,因此不管是不是和源点连通的边它都会得到更新;但是SPFA算法不一样,它相当于采用了BFS,因此遍历到的结点都是与源点连通的,因此如果你要求的n和源点不连通,它不会得到更新,还是保持的0x3f3f3f3f。

    4. Bellman_ford算法可以存在负权回路,是因为其循环的次数是有限制的因此最终不会发生死循环;但是SPFA算法不可以,由于用了队列来存储,只要发生了更新就会不断的入队,因此假如有负权回路请你不要用SPFA否则会死循环。

    5. 由于SPFA算法是由Bellman_ford算法优化而来,在最坏的情况下时间复杂度和它一样即时间复杂度为 O(nm),假如题目时间允许可以直接用SPFA算法去解Dijkstra算法的题目。

  • ⭐️限制

    没有负环就可以使用,99%的题没有负环,但是可以用来判断负环

    阴险的出题人会把SPFA卡掉,使达到最坏O(nm), 网格图可能会卡
    在这里插入图片描述

  • ⭐️步骤

    由Bellman-Ford算法用宽搜做优化

    queue <- 1
    while  queue 不空 //队列里面放的是 待更新的点  t=q.frontq.pop()更新t的所有边 t-w->bqueue <- b
    

4.2图示

给定一个有向图,如下,求A~E的最短路。

1.png

源点A首先入队,然后A出队,计算出到BC的距离会变短,更新距离数组,BC没在队列中,BC入队

2.png

B出队,计算出到D的距离变短,更新距离数组,D没在队列中,D入队。然后C出队,无点可更新。

3.png

D出队,计算出到E的距离变短,更新距离数组,E没在队列中,E入队。

4.png

E出队,此时队列为空,源点到所有点的最短路已被找到,A->E的最短路即为8

5.png

4.2模板题

  • ⭐️题目

    链接: 851. spfa求最短路 - AcWing题库

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

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

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

    输入格式

    第一行包含整数 n 和 m。

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

    输出格式

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

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

    数据范围

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

    输入样例:

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

    输出样例:

    2
    
  • ⭐️题解

    #include
    using namespace std;const int N=1e5+10;int n,m;
    int h[N],e[N],ne[N],w[N],idx;
    int dist[N];
    bool st[N];  //表明该点已经在 队列里面了,防止队列里面出现重复元素void add(int a,int b,int c)
    {e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
    }int spfa()
    {memset(dist,0x3f,sizeof dist);dist[1]=0;queue q;q.push(1);  //把起点放进去st[1]=true;  while(q.size())  //队列不空{int t=q.front();q.pop();    //取出队头st[t]=false;  //该点离开 队列,状态改变for(int i=h[t];i!=-1;i=ne[i])  //遍历  该点的相邻顶点{int j=e[i];if(dist[j]>dist[t]+w[i])  //更新{dist[j]=dist[t]+w[i];if(!st[j])  //该点没有在队列里面,则放进去,否则会出现重复{q.push(j);st[j]=true;}}}}if(dist[n]>0x3f3f3f3f/2) return 0x3f3f3f3f/2+1; //记住有负权边的最短路,最后都这样处理return dist[n];}int main()
    {scanf("%d%d",&n,&m);memset(h,-1,sizeof h);  //链表初始化for(int i=0;iint a,b,w;scanf("%d%d%d",&a,&b,&w);add(a,b,w);}int t=spfa();if(t>0x3f3f3f3f/2) puts("impossible");else printf("%d",t);return 0;
    }
    

4.4判断负环

  • ⭐️思路

    cnt记录每个点到起点的边数,当cnt[i] >= n 表示出现了边数>=结点数,必然有环,而且一定是负环

    根据抽屉原理,可得。

    抽屉原理:如果每个抽屉代表一个集合,每一个苹果就可以代表一个元素,假如有n+1个元素放到n个集合中去,其中必定有一个集合里至少有两个元素。

  • ⭐️题目

    链接: AcWing 852. spfa判断负环 - AcWing

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

    请你判断图中是否存在负权回路。

    输入格式

    第一行包含整数 n 和 m。

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

    输出格式

    如果图中存在负权回路,则输出 Yes,否则输出 No

    数据范围

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

    输入样例:

    3 3
    1 2 -1
    2 3 4
    3 1 -4
    

    输出样例:

    Yes
    
  • ⭐️题解

    #include
    using namespace std;const int N=2010,M=1e4+10;int n,m;
    int h[N],e[M],w[M],ne[M],idx;
    int dist[N],cnt[N]; 
    //dist 存的是当前从1号点到n号点的长度
    //cnt 表示从1到x的最短路径中经过的点数
    bool st[N];void add(int a,int b,int c)
    {e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
    }bool spfa()
    {queue q;  //这里不用初始化距离,因为咱不是求最短路滴for(int i=1;i<=n;i++)  //把所有的点放进队列里面去,可能存在负环,无法到达起点1,所有遍历所有点{st[i]=true;q.push(i);}while(q.size()){int t=q.front();q.pop();st[t]=false;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(dist[j]>dist[t]+w[i])  //更新{dist[j]=dist[t]+w[i];cnt[j]=cnt[t]+1;  //当前到该点的边数+1if(cnt[j]>=n) return true;  //cnt>=n,说明有点走了两遍,一定存在负环if(!st[j]){st[j]=true;  //入队q.push(j);}}}}return false;  //走到这了,函数还没结束,意味着边数一直小于结点数,不存在负环
    }int main()
    {scanf("%d%d",&n,&m);memset(h,-1,sizeof h);while(m--){int a,b,w;scanf("%d%d%d",&a,&b,&w);add(a,b,w);}if(spfa()) puts("Yes");else puts("No");return 0;
    }
    

5.Floyd算法

5.1思想

不能应用于含负权回路的图

d[i][j] //邻接矩阵,存的是 i到j 的最短路问题for(k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)d[i][j]=min(d[i][j],d[i][k]+d[k][j])  //更新

5.2模板题

  • ⭐️题目

    链接: 854. Floyd求最短路 - AcWing题库

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

    再给定 k 个询问,每个询问包含两个整数 x 和 y,表示查询从点 x 到点 y 的最短距离,如果路径不存在,则输出 impossible

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

    输入格式

    第一行包含三个整数 n,m,k。

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

    接下来 k 行,每行包含两个整数 x,y,表示询问点 x 到点 y 的最短距离。

    输出格式

    共 k 行,每行输出一个整数,表示询问的结果,若询问两点间不存在路径,则输出 impossible

    数据范围

    1≤n≤200,
    1≤k≤n2n^2n2
    1≤m≤20000,
    图中涉及边长绝对值均不超过 10000。

    输入样例:

    3 3 2
    1 2 1
    2 3 2
    1 3 1
    2 1
    1 3
    

    输出样例:

    impossible
    1
    
  • ⭐️题解

    #include
    using namespace std;const int N=210,INF=1e9;int n,m,k;
    int d[N][N];void floyd()
    {for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
    }int main()
    {scanf("%d%d%d",&n,&m,&k);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(i==j) d[i][j]=0;else d[i][j]=INF;while(m--){int a,b,w;scanf("%d%d%d",&a,&b,&w);d[a][b]=min(d[a][b],w);}floyd();while(k--){int a,b;scanf("%d%d",&a,&b);if(d[a][b]>INF/2) puts("impossible");else printf("%d\n",d[a][b]);}return 0;
    }
    

五种算法总结🎈

⭐️Dijkstra-朴素 O(n^2)

  1. 初始化距离数组, dist[1] = 0, dist[i] = inf;
  2. for n次循环 每次循环确定一个min加入S集合中,n次之后就得出所有的最短距离
  3. 将不在S中dist-min的点->t
  4. t->S加入最短路集合
  5. 用t更新到其他点的距离

⭐️Dijkstra-堆优化 O(mlogn)

  1. 利用邻接表,优先队列
  2. 在priority_queue[HTML_REMOVED], greater[HTML_REMOVED] > heap;中将返回堆顶
  3. 利用堆顶来更新其他点,并加入堆中类似宽搜

⭐️Bellman-ford O(nm)

  1. 注意连锁想象需要备份, struct edge { int a , b , c } Edge[M];
  2. 初始化 dist , 松弛 dist [x.b] = min ( dist [x.b] , backup [x.a] + x.w ) ;
  3. 松弛 k 次,每次访问 m 条边,有边数限制

⭐️SPFA O(n)~O(nm)

  1. 利用队列优化仅加入修改过的地方
  2. for k次
  3. for 所有边利用宽搜模型去优化 Bellman-ford 算法
  4. 更新队列中当前点的所有出边

⭐️Floyd O(n3n^3n3)

  1. 初始化d
  2. k, i, j 去更新d

部分解释及图示转自acwing
Alt

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...