题解: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
【核心思想】
-
问题分析:给定有向图 \(G=(V,E)\),其中 \(|V|=n\),\(|E|=m\),边权可能为负数,但保证不存在负权回路。需要回答 \(k\) 个询问,每个询问求从点 \(x\) 到点 \(y\) 的最短距离。这是一个多源最短路径问题,需要求任意两点之间的最短距离,Floyd 算法是最直接的选择。
-
算法选择:
- Floyd-Warshall 算法:基于动态规划思想,通过中间节点逐步更新最短路径
- 距离矩阵 \(dist[][]\):\(dist[i][j]\) 表示从节点 \(i\) 到节点 \(j\) 的当前最短距离
- 状态转移方程:\(dist[i][j] = \min(dist[i][j], dist[i][k] + dist[k][j])\),表示考虑经过中间节点 \(k\) 的路径是否更短
-
关键步骤:
- 初始化:
- 读取 \(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]\) 作为答案
- 初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(n^3)\),三重循环,每层最多 \(n\) 次
- 空间复杂度:\(O(n^2)\),需要存储 \(n \times n\) 的距离矩阵
- 适用于多源最短路径问题,节点数 \(n\) 较小(通常 \(n \leq 500\))的场景
-
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
浙公网安备 33010602011771号