题目描述
- 给定一个
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
。
数据范围
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;
}