题解:AcWing 854 Floyd求最短路

【题目来源】

AcWing: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。

【输入样例】

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

【输出样例】

impossible
1

【核心思想】

  1. 问题分析:给定有向图 \(G=(V,E)\),其中 \(|V|=n\)\(|E|=m\),边权可能为负数,但保证不存在负权回路。需要回答 \(k\) 个询问,每个询问求从点 \(x\) 到点 \(y\) 的最短距离。这是一个多源最短路径问题,需要求任意两点之间的最短距离,Floyd 算法是最直接的选择。

  2. 算法选择

    • Floyd-Warshall 算法:基于动态规划思想,通过中间节点逐步更新最短路径
    • 距离矩阵 \(dist[][]\)\(dist[i][j]\) 表示从节点 \(i\) 到节点 \(j\) 的当前最短距离
    • 状态转移方程\(dist[i][j] = \min(dist[i][j], dist[i][k] + dist[k][j])\),表示考虑经过中间节点 \(k\) 的路径是否更短
  3. 关键步骤

    • 初始化
      • 读取 \(n\)(节点数)、\(m\)(边数)、\(k\)(询问数)
      • 初始化距离矩阵:
        • \(dist[i][i] = 0\)(节点到自身距离为0)
        • \(dist[i][j] = INF\)\(i \neq j\) 时初始化为无穷大)
      • 读入 \(m\) 条边 \((x, y, z)\),更新 \(dist[x][y] = \min(dist[x][y], z)\)(处理重边,保留最小权重)
    • Floyd 算法(三重循环):
      • 外层循环 \(k\)\(1\)\(n\):枚举中间节点
      • 中层循环 \(i\)\(1\)\(n\):枚举起点
      • 内层循环 \(j\)\(1\)\(n\):枚举终点
      • 状态转移:\(dist[i][j] = \min(dist[i][j], dist[i][k] + dist[k][j])\)
    • 处理询问
      • 对于每个询问 \((x, y)\)
      • \(dist[x][y] \geq INF/2\),输出 impossible(不可达)
      • 否则输出 \(dist[x][y]\) 作为答案
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n^3)\),三重循环,每层最多 \(n\)
    • 空间复杂度:\(O(n^2)\),需要存储 \(n \times n\) 的距离矩阵
    • 适用于多源最短路径问题,节点数 \(n\) 较小(通常 \(n \leq 500\))的场景
  5. Floyd 算法的核心思想

    • 动态规划思想\(dist[k][i][j]\) 表示只使用前 \(k\) 个节点作为中间节点时,\(i\)\(j\) 的最短距离
    • 状态转移:考虑是否经过节点 \(k\) 作为中间点,\(dist[k][i][j] = \min(dist[k-1][i][j], dist[k-1][i][k] + dist[k-1][k][j])\)
    • 滚动数组优化:由于 \(dist[k]\) 只依赖于 \(dist[k-1]\),可以使用二维数组滚动更新
    • 最优子结构:最短路径的子路径也是最短路径
    • 适用于多源最短路径:一次计算得到所有节点对之间的最短距离,适合多次查询场景
    • 适用于节点数较少的全源最短路径问题,如传递闭包、最小环等问题

【解题思路】

【算法标签】

Floyd

【代码详解】

#include <bits/stdc++.h>  // 包含所有标准库头文件
using namespace std;

const int N = 210, INF = 1e9;  // 定义常量N为210,INF为一个很大的数(表示无穷大)
int dist[N][N];  // 定义一个二维数组dist,用于存储任意两点之间的最短距离
int n, m, q;  // n表示图中节点的数量,m表示边的数量,q表示查询的数量

// Floyd-Warshall算法实现
void floyd()
{
    // 三重循环,k是中间节点,i是起点,j是终点
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                // 更新i到j的最短距离,考虑通过k节点的情况
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}

int main()
{
    // 输入节点数n,边数m,查询数q
    cin >> n >> m >> q;

    // 初始化dist数组
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (i == j)
                dist[i][j] = 0;  // 节点到自身的距离为0
            else
                dist[i][j] = INF;  // 其他节点之间的距离初始化为无穷大

    // 输入m条边,并更新dist数组
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;  // 输入边的起点a,终点b,权重c
        dist[a][b] = min(dist[a][b], c);  // 更新a到b的最短距离,保留最小的权重
    }

    // 调用Floyd-Warshall算法计算所有节点对之间的最短路径
    floyd();

    // 处理q个查询
    while (q--) {
        int a, b;
        cin >> a >> b;  // 输入查询的起点a和终点b
        int ans = dist[a][b];  // 获取a到b的最短距离

        // 如果最短距离大于等于INF/2,说明a和b之间没有路径(或路径不可达)
        if (ans >= INF / 2)
            cout << "impossible" << endl;  // 输出"impossible"
        else
            cout << ans << endl;  // 否则输出最短距离
    }

    return 0;  // 程序结束
}
#include <bits/stdc++.h>
using namespace std;

const int N = 210;        // 最大节点数
const int INF = 1e9;      // 无穷大值(表示不可达)

int n;                    // 节点数量
int m;                    // 边数量
int Q;                    // 查询次数
int d[N][N];             // 距离矩阵,d[i][j]表示节点i到节点j的最短距离

/**
 * Floyd-Warshall算法:计算所有节点对之间的最短路径
 * 动态规划思想:通过中间节点逐步优化最短路径
 */
void floyd()
{
    // 三重循环:依次考虑每个节点作为中间节点
    for (int k = 1; k <= n; k++)
    {
        // 遍历所有起点
        for (int i = 1; i <= n; i++)
        {
            // 遍历所有终点
            for (int j = 1; j <= n; j++)
            {
                // 状态转移方程:比较直接路径和通过中间节点k的路径
                // d[i][j] = min(当前最短路径, 经过k的路径)
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
            }
        }
    }
}

int main()
{
    // 输入节点数、边数、查询次数
    scanf("%d%d%d", &n, &m, &Q);

    // 初始化距离矩阵
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (i == j)
            {
                // 节点到自身的距离为0
                d[i][j] = 0;
            }
            else
            {
                // 初始时节点间距离为无穷大(表示不可达)
                d[i][j] = INF;
            }
        }
    }

    // 输入所有边的信息
    while (m--)
    {
        int a, b, w;  // a:起点, b:终点, w:权重
        scanf("%d%d%d", &a, &b, &w);

        // 处理重边:取最小权重的边
        d[a][b] = min(d[a][b], w);
    }

    // 执行Floyd算法计算所有节点对的最短路径
    floyd();

    // 处理每个查询
    while (Q--)
    {
        int a, b;  // 查询起点a到终点b的最短距离
        scanf("%d%d", &a, &b);

        // 判断是否可达:由于可能存在负权边,使用INF/2作为阈值
        if (d[a][b] > INF / 2)
        {
            // 不可达的情况
            puts("impossible");
        }
        else
        {
            // 可达的情况,输出最短距离
            printf("%d\n", d[a][b]);
        }
    }

    return 0;
}

【运行结果】

3 3 2
1 2 1
2 3 2
1 3 1
2 1
imposssible
1 3
1
posted @ 2026-06-16 10:14  团爸讲算法  阅读(5)  评论(0)    收藏  举报