Floyd算法详(cha)解

Floyd 算法应该是最基本,最简单,也是最暴力的最短路算法解法,可是对于一些点数很小的题目,Floyd的表现仍是很优秀的,咱们先来看一道例题算法

题目描述给你一个有 \(n\) (\(n\leq 100\)) 个点以及 \(m\) (\(m\leq 800\)) 条双向边的图,求出全部点之间的最短路。
输入格式:第一行两个正整数 \(n\),\(m\),接下来 \(m\) 行,每行三个正整数 \(u\),\(v\),\(w\),表示 \(u\)\(v\) 之间有一条代价(长度)为 \(w\) 的边。
输出格式:输出共 \(n\) 行,每行 \(n\) 个正整数,第 \(i\) 行第 \(j\) 个数,表示点 \(i\) 到点 \(j\) 之间的最短路。
样例输入
5 6
1 3 7
2 4 5
2 3 1
4 3 2
1 5 8
5 2 4
样例输出
0 8 7 9 8
8 0 1 3 4
7 1 0 2 5
9 3 1 0 7
8 4 5 7 0数组

对于任何一个操做,咱们均可以分红三个部分:1.选择操做容器,2.初始化,3.更新,操做。spa

好比这一题,让咱们求出 全部点 之间的最短路,并以一个 矩阵型 输出,因此咱们能够考虑就如题目中的那样,用这个临接矩阵来储存,咱们把这个矩阵称为 \(\operatorname{dis}\),用 \(\operatorname{dis[i][j]}\) 来表示 \(\operatorname{i\sim j}\) 之间的最短路径。3d

知道了容器,咱们就要考虑如何初始化。由于题目让咱们求全部点之间的最短路,因此咱们能够初始化 \(\operatorname{dis[i][i]=0}\) (能够理解为,我到我本身,至关于没动,因此我不须要耗费任何代价),而后每输入两个点 \(u\),\(v\),就将他们输入的代价,做为最初的 \(\operatorname{dis[u][v]}\) 设置为代价 \(w\),由于在如今看来,这条路是最短的,咱们没法从其余地方更新。而后咱们考虑一个问题,若是这不是一个 彻底图,也就是说,若是并非每两个点之间都有一条连边,有一些数对(i,j)是没有直接通路的,咱们要怎么办?其实很是简单,若是两个点一开始没有直接的连边,咱们就能够将它设置成一个不会被重复的值(通常为正无穷或者负无穷,看求的东西),也就是 \(\operatorname{dis[u][v]=Inf}\),因而咱们在最开始,就赋值整个数组为正无穷,这样就能够很方便的预处理完成最初的每两个点之间的最短路了。code

可是,咱们这样并不能直接求出这整个图每两点之间的最短路,由于确定有一些点没有更新到,而且有一些点之间的最短路点在最后不必定是最短的,这个很好证实,我就不赘述了。因而咱们考虑更新,咱们拿样例打比方,咱们先建出这个图:
blog

而后按照刚刚的方法初始化一下这个矩阵每一个点之间的最初的最短路,大概是这个样子的:get

0 ∞ ∞ ∞ ∞ --> 0 ∞ 7 ∞ 8
∞ 0 ∞ ∞ ∞ --> ∞ 0 1 5 4
∞ ∞ 0 ∞ ∞ --> 7 1 0 2 ∞
∞ ∞ ∞ 0 ∞ --> ∞ 5 2 0 ∞
∞ ∞ ∞ ∞ 0 --> 8 4 ∞ ∞ 0class

而后就是这个算法最重要的部分了,咱们考虑如何更新两个点之间的最短路,来看下面这个简化的图:

在这个图中,\(1\sim3\) 之间初试化的最短路是 \(10000\),显然,当咱们从新选择 \(1\sim 2\sim3\) 这条路的时候,代价就减少到了 \(3\),比以前那条道路更优秀。这就至关因而在点 \(1\)\(3\) 之间,找到一个新的点加入,用 \(1\sim 2\)\(2\sim3\) 来更新这个最短路,能够算做是一种 区间dp 的思想。Floyd 的核心思想就是这个,就是在 两点之间加上一个点 而后和以前的最短路做比较,而后不断更新,达到求出全源最短路的效果。容器

咱们再将这种算法放回原样例中去验证一下,咱们从1-n 枚举每一个节点 \(k\) ,用来更新两个点之间是否还有更短的路径。咱们从 \(1\) 开始,咱们来看一遍。方法

用 1 更新:
0 ∞ 7 ∞ 8 --> 0 ∞ 7 ∞ 8
∞ 0 1 5 4 --> ∞ 0 1 5 4
7 1 0 2 ∞ --> 7 1 0 2 ∞
∞ 5 2 0 ∞ --> ∞ 5 2 0 ∞
8 4 ∞ ∞ 0 --> 8 4 ∞ ∞ 0

用 2 更新:
0 ∞ 7 ∞ 8 --> 0 ∞ 7 ∞ 8
∞ 0 1 5 4 --> ∞ 0 1 5 4
7 1 0 2 ∞ --> 7 1 0 2 5
∞ 5 2 0 ∞ --> ∞ 5 2 0 9
8 4 ∞ ∞ 0 --> 8 4 5 9 0

用 3 更新:
0 ∞ 7 ∞ 8 --> 0 8 7 9 8
∞ 0 1 5 4 --> 8 0 1 3 4
7 1 0 2 5 --> 7 1 0 2 5
∞ 5 2 0 9 --> 9 3 2 0 7
8 4 ∞ ∞ 0 --> 8 4 5 7 0

用 4 更新:
0 8 7 9 8 --> 0 8 7 9 8
8 0 1 3 4 --> 8 0 1 3 4
7 1 0 2 5 --> 7 1 0 2 5
9 3 2 0 7 --> 9 3 2 0 7
8 4 5 7 0 --> 8 4 5 7 0

用 5 更新:
0 8 7 9 8 --> 0 8 7 9 8
8 0 1 3 4 --> 8 0 1 3 4
7 1 0 2 5 --> 7 1 0 2 5
9 3 2 0 7 --> 9 3 2 0 7
8 4 5 7 0 --> 8 4 5 7 0

(你们能够配着图来理解)[加粗数字为更新过的最短路]

看起来没什么问题实际也没什么问题,因而咱们开是码代码吧。

先是初始化:

memset(dis,0x3f3f3f3f,sizeof(dis));
for(int i=1;i<=n;i++)dis[i][i]=0;
for(int i=1;i<=n;i++)int u,v,w,scanf("%d%d%d",&u,&v,&w),dis[u][v]=w;

第一行就是初始化 \(\operatorname{dis}\) 都为正无穷,0x3f3f3f3f是16进制中的一个数,差很少接近了 INT_MAX 了。
第二行就是本身到本身的最短路
第三行是输入与每两个点之间的最短路初始化。

而后就是 Floyd 啦:

for(int k=1;k<=n;k++)//首先枚举中间加入的点,否则会出错
      for(int i=1;i<=n;i++)//而后i,j是枚举每一个点,算 i~j 之间的最短路 
            for(int j=1;j<=n;j++)
                 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);//Floyd的状态转移方程,这是精华,必定要记牢

这些上面已经讲过了,没懂的能够多看几遍。
看懂了的完整代码应该不难写了,因此整块的代码就不放了。

下面给出几个例题:

相关文章
相关标签/搜索