# AcWing 854. Floyd 求最短路
给定一个 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≤n2 1≤m≤20000, 图中涉及边长绝对值均不超过 10000。
输入样例
3 3 2 1 2 1 2 3 2 1 3 1 2 1 1 3
输出样例
impossible 1
# 题目描述
给定一个有 n 个点和 m 条边的有向图,图中可能存在重边和自环,并且存在负权边。问题是让我们求任意两点间的最短距离。
# Floyd 思路
Floyd 使用动态规划的思想,能在 O (n^3) 的时间复杂度下求出任意两点间的最短路径,可以存在负权边,但是不能存在负权回路。
状态表示:d (k, i, j) 表示从 i 点开始,只经过 1~k 这些点,最后到达 j 的最小距离。
状态转移:d (k ,i, j) 可以由 d (k-1, i, k) + d (k-1, k, j) 转换而来。表示的含义是只经过 1~k-1 这些点,并且从 i 到 k 再从 k 到 j。我们发现第一维 k 只能从 k-1 转换而来,因此可以忽略,所以有转移方程:d (i, j)=d (i, k)+d (k, j)。
首先 k 表示枚举出的所有顶点,那么从 i 到 j 只能存在两种情况:
1、直接就从 i 到 j;
2、从 i 先到 k 再到 j;
那么我们枚举出所有的顶点 k,判断从 i 到 k 再到 j 是否比 i 直接到 j 距离更短,即检查 d (i,k) + d (k,j)<d (i,j) 是否成立。如果成立就更新 d (i, j)。
如此,当所有的顶点 k 都被枚举过之后,d 中存储的就是任意两点间的最短距离。
# Code
import sys | |
n, M, K = map(int, input().split()) | |
d = [[float('inf')] * (n + 1) for _ in range(n + 1)] | |
# 每一条边自己到自己的距离都是 0 | |
for i in range(1, n + 1): | |
d[i][i] = 0 | |
for _ in range(M): | |
x, y, z = map(int, list(sys.stdin.readline().strip().split())) | |
# 如果出现重边的话要取最短的一条 | |
d[x][y] = min(d[x][y], z) | |
# Floyd 最关键的地方,外层循环是 k。 | |
for k in range(1, n + 1): | |
for i in range(1, n + 1): | |
for j in range(1, n + 1): | |
d[i][j] = min(d[i][j], d[i][k] + d[k][j]) | |
for _ in range(K): | |
x, y = map(int, list(sys.stdin.readline().strip().split())) | |
print('impossible' if d[x][y] == float('inf') else d[x][y] |