算法刷题笔记 Floyd(弗洛伊德)求最短路 (带有超级详细注释的C++实现)

62 篇文章 3 订阅
55 篇文章 1 订阅

题目描述

  • 给定一个n个点m条边的有向图,图中可能存在重边和自环,边权可能为负数。
  • 再给定k个询问,每个询问包含两个整数xy,表示查询从点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 ≤ n²
  • 1 ≤ m ≤ 20000,
  • 图中涉及边长绝对值均不超过10000

基本思路

  • 弗洛伊德算法的功能:求解有向图中的多源最短路问题,即从不同的点出发到达指定点的最短路径长度。
  • 弗洛伊德算法的过程:总体而言,弗洛伊德的逻辑较为简单,过程如下:
    • 使用邻接矩阵存储有向图中所有的边,初始情况下除了将邻接矩阵中对角线上的元素初始化为0之外,其他元素都初始化为正无穷;
    • 算法的核心是三重循环,循环形式如下,其中d(i, j)表示邻接矩阵中第i行第j列的元素。算法结束后,d(i, j)存储的就是从编号为i的点到编号为j的点的最短路径的长度。
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))
  • 弗洛伊德算法的原理:弗洛伊德算法是基于动态规划思想的,该动态规划中每一个时刻的状态为d(k, i, j),表示从点i开始,只经过1-k之内的中间点,到达j点的最短路径长度。
  • 弗洛伊德算法的时间复杂度:三重循环,因此总体时间复杂度是O(n³),其中n表示有向图中的点的个数。
  • 算法适用情况:该算法可以用于处理含有负权边的图,但是仍然不能用于处理带有负权回路的图。

代码实现

#include <cstdio>
#include <algorithm>
#include <climits>
using namespace std;

// 【变量定义】有向图中的点的个数、边的条数和询问的个数
int n, m, k;
// 【变量定义】有向边的起点编号、终点编号和边长(权重)
int x, y, z;
// 【辅助常量定义】有向图中点的个数的上限
const int N = 210;
// 【变量定义】用于表示有向图的邻接矩阵
int adjacent_matrix[N][N];


// 【函数定义】对邻接矩阵进行初始化的函数
void init_adjacent_matrix(void)
{
    // 将邻接矩阵对角线上的元素都初始化为0,其他位置元素初始化为无穷大
    // 表示有向图中每个点到自身的最短路径长度一定为0,到其他点的路径长度不确定
    for(int row = 1; row <= n; ++ row)
    {
        for(int col = 1; col <= n; ++ col)
        {
            // 这里使用INT_MAX的一半是为了防止计算过程中溢出
            if(row != col) adjacent_matrix[row][col] = INT_MAX >> 1;
            else adjacent_matrix[row][col] = 0;
        }
    }
}
// 【函数定义】基于弗洛伊德算法求解有向图中任意两点之间的最短路径的函数
void Floyd(void)
{
    // 【算法主体】使用弗洛伊德算法求解有向图中任意两点之间的最短路径长度
    for(int k = 1; k <= n; ++ k)
    {
        for(int i = 1; i <= n; ++ i)
        {
            for(int j = 1; j <= n; ++ j) 
            {
                adjacent_matrix[i][j] = min(adjacent_matrix[i][j], adjacent_matrix[i][k] + adjacent_matrix[k][j]);
            }
        }
    }
}
// 【函数定义】查询有向图中任意两点之间的最短路径长度的函数
inline void query_shortestPath(int start, int end)
{
    // 如果两个点之间存在路径,则直接输出最短路径长度
    if(adjacent_matrix[start][end] <= INT_MAX >> 2) printf("%d\n", adjacent_matrix[start][end]);
    // 如果两个点之间不存在路径,则输出"impossible"
    else printf("impossible\n");
}

int main(void)
{
    // 【变量输入】输入有向图中点的个数、边的条数和询问的个数
    scanf("%d%d%d", &n, &m, &k);
    // 【初始化】通过自定义的函数初始化邻接矩阵
    init_adjacent_matrix();
    // 【变量输入】输入有向图中每一条边的起点编号、终点编号和边长(权重)
    for(int i = 0; i < m; ++ i)
    {
        scanf("%d%d%d", &x, &y, &z);
        // 将有向边的信息通过邻接矩阵进行存储(由于可能存在重边,因此用邻接矩阵存储时需要存储最短的边)
        adjacent_matrix[x][y] = min(adjacent_matrix[x][y], z);
    }
    // 【求解最短路径】使用自定义的函数求解各点之间的最短路径长度
    Floyd();
    // 【查询输出部分】每次输入需要查询最短路径的起点编号和终点编号,然后进行查询
    for(int i = 0; i < k; ++ i)
    {
        scanf("%d%d", &x, &y);
        // 使用自定义的函数进行查询
        query_shortestPath(x, y);
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值