笔记3:最短路

· · 个人记录

笔记3:最短路

前言:

没有前言(⊙o⊙)...

补充:

首先你要知道一个事实:如果图中有一个环,这个环上所有边权加起来是负数(简称负环),则不存在最短路。(你可以绕着这个环一直走下去,越走越短)

在开始之前,我需要介绍一个东西:三角形不等式

众所周知,三角形两边之和大于第三边。用不等式可以表示为a+b>c

下文提到的松弛操作全部与此有关

正文:

最短路的求法有以下几种:

以下将逐一讲到 这些算法都基于一个操作:**松弛** 松弛操作其实是这样的:如图(画图板勿喷)好孩子不要学啊 ![松弛操作的图](https://cdn.luogu.com.cn/upload/pic/53467.png) 你要从$A$点走到$C$点,现在你面前有一条路(红色),先从$A$到$B$再从$B$到$C

然而鲁迅说过,其实世上本没有路,走的人多了,也便成了路

于是现在就有了另一条路,可以直接从AC,于是目前你会如何选择?

显然图上的三个箭头的长度满足三角形不等式,故

AB+BC<AC

因此你就会改变你原来的想法,不走AB,BC,而是AC

这就叫松弛操作

1、关于Floyd算法

主要思想:DP

时间复杂度:O(N^3)

对图的要求:没有负环

本算法只适用于邻接矩阵,可以一次性求出所有点对间的最短路。

算法过程:

先初始化所有点自己到自己的距离为0,每条边的起点到终点的距离为边权,其他INF,然后枚举开始点,中间点以及结束点。以刚才的图为例,如果现在经过草坪中的一个点k,距离就会比老老实实走大路短,就选择踩草坪而不是走大路。故状态转移方程为

dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])

特别需要注意的是,k作为中间点,要放在最外层循环

代码:

 void floyd() { 
   for(int i=1;i<=n;i++) //初始化
   { 
     for(int j=1;j<=n;j++) 
     { 
       if(i==j) dis[i][j]=0; //自己到自己距离为0
       else if(a[i][j])  dis[i][j]=a[i][j]; //如果两点间直接相连,则设为边权
       else dis[i][j]=0x7fffff/2; //其他赋极大值
     }
   }

   //算法主干:
   for(int k=1;k<=n;k++) //枚举中间点
     for(int i=1;i<=n;i++)  //枚举起点
       for(int j=1;j<=n;j++)  //枚举终点
         dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); //状态转移
 }

本算法的优点和缺点:

完结撒花