Floyd 算法思想

48 篇文章 0 订阅
8 篇文章 0 订阅

今天总结的是图的最短路径的另外一种算法---弗洛伊德算法。与前面迪杰斯特拉算法不同的是,弗洛伊德算法求的是图中任意一对顶点之间的最短路径,当然,仍然针对有向带权图。

  我们就先直接进入算法的演算过程吧~大家可以在这个演算过程中,体会到弗洛伊德算法是如何表示任意两点间的最短路径的。

  算法准备:

边权值非负

可以处理环的问题

  1、图的邻接矩阵(与以往不同的是,主对角线,也就是自身到自身,这次我们不使用无穷大表示,而是采用0。书上和习题中是这样的,我们就入乡随俗吧~)

  2、二维数组A,用于存放任意一对顶点之间的最短路径权值。

  3、二维数组Path,用于存放任意一对顶点之间的最短路径。每个单元格的内容表示从i点到j点途经的顶点。

算法过程:

  1、初始化过程:将邻接矩阵copy到二维数组A中,将二维数组Path中所有元素填充为-1(都没有开始寻找,哪里来的中间顶点呢)。、

2、列出顶点的所有可能二元组,自己到自己不算。这里为

 {0,1},{0,2},{0,3},{1,0},{1,2},{1,3},{2,0},{2,1},{2,3},{3,0},{3,1},{3,2}

  3、选择编号为0的点为中间点,从【2】中二元组集合的第一个元素开始,执行以下过程

       3.1:用i,j两个变量分别指向二元组里的两个元素,比如{0,1}这个二元组,i指向0;j指向1

       3.2:判断A[i][j] > A[i][0] + A[0][j]吗?如果表达式为真,进入3.3;若为假,则结束本次过程,进入下一个二元组

       3.3:更新A[i][j]的值为A[i][0] + A[0][j],Path[i][j]的值为0

分析:步骤3其实挺形象的,也是算法核心。二维数组A就是图邻接矩阵的copy,我们以行的角度来看这个矩阵,比如A[0][1] = 5,表示从顶点0到顶点1有一条边相连,权值为5。而在这次循环中,我们选择了0为中间点,把步骤3.2的表达式翻译过来就是说:从i点到j点的代价会大于从i点到0点,再从0点到j点的代价吗?是不是非常形象地把顶点0作为中间点这个思想表示出来了呢?如果满足3.2中的表达式,就表示我经过中间点0到目的地比我直接去目的地的代价还要来的少,于是我们更新A[i][j]的值为新求出的这个比原来小的代价的值,Path[i][j]的值更新为0,就表示从i点到j点,要经过0点。非常巧妙地记录了路径。(PS.这怎么有点向我以前看过的深度优先搜索走迷宫村路径的方式呀~,这个与数学中的定积分的某一条性质也特别像。)

   4、重复步骤【3】,直到所有的顶点都做过一次中间点为止。

最后两个矩阵的值如下:

下面一个步骤就是找出两点间的路径了,比如顶点1到顶点0,我们看数组Path

Path[1][0] = 3,说明顶点3是途径顶点

Path[3][0] = 2,说明顶点2是途径顶点

Path[2][0] = -1,说明顶点2到顶点0没有途径顶点,也就是说,可以由顶点2直接到顶点0,即它们有边连接。

所以,序列为1->3->2->0,显然,这是一个逐层递进,递归的过程。
 

#include<cstdio>
using namespace std;
#define INF 1e9
const int maxn=100+10;
int n,m;//点数,边数,点从0到n-1编号
int dist[maxn][maxn];//记录距离矩阵
int path[maxn][maxn];//path[i][j]=x表示i到j的路径上(除i外)的第一个点是x.
void init()
{
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    {
        dist[i][j] = i==j?0:INF;//其实这里d[i][j]应该还要通过输入读数据的
        path[i][j]=j;
    }
 
    //读取其他dist[i][j]的值
}
void floyd()
{
    for(int k=0;k<n;k++)
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    if(dist[i][k]<INF && dist[k][j]<INF )
    {
        if(dist[i][j]>dist[i][k]+dist[k][j])
        {
            dist[i][j] = dist[i][k]+dist[k][j];
            path[i][j] = path[i][k];
        }
        else if(dist[i][j] == dist[i][k]+dist[k][j] &&path[i][j]>path[i][k])
        {
            path[i][j] = path[i][k];  //最终path中存的是字典序最小的路径
        }
    }
}
 
int main()
{
    //读n和m
    init();
    //读m条边
    floyd();
    //输出所求最短路径距离
    return 0;
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值