【Floyd】Shortest Routes II
题目描述
There are n cities and m roads between them. Your task is to process q queries where you have to determine the length of the shortest route between two given cities.
输入
The first input line has three integers n, m and q: the number of cities, roads, and queries.
Then, there are m lines describing the roads. Each line has three integers a, b and c: there is a road between cities a and b whose length is c. All roads are two-way roads.
Finally, there are q lines describing the queries. Each line has two integers a and b: determine the length of the shortest route between cities a and b.
Constraints
1 ≤ n ≤ 500
1 ≤ m ≤ n2
1 ≤ q ≤ 105
1 ≤ a,b ≤ n
1 ≤ c ≤ 109输出
Print the length of the shortest route for each query. If there is no route, print -1 instead.
样例输入
复制
4 3 5 1 2 5 1 3 9 2 3 3 1 2 2 1 1 3 1 4 3 2
样例输出
复制
5 5 8 -1 3
为什么不是换根 DP?
- 换根 DP 是一种针对树结构的算法(比如没有环的图),主要用于高效计算 “所有节点作为根时的某种属性”(比如每个节点到其他所有节点的距离和)。
- 但这个问题中的 “城市和道路” 可能形成环(比如 A→B→C→A),不是树结构,所以换根 DP 不适用。
为什么用弗洛伊德算法?
- 弗洛伊德算法的作用是:一次性计算出所有节点对之间的最短路径(比如城市 1 到城市 2、城市 1 到城市 3、城市 2 到城市 3…… 全算出来)。
Floyd-Warshall 算法能够计算所有节点对之间的最短路径,核心在于它利用了动态规划的思想,通过逐步引入中间节点来松弛(优化)任意两点间的距离。
核心原理:通过中间节点优化路径
假设我们有三个节点 i
、j
、k
,算法的基本思路是:
“从 i
到 j
的最短路径,要么不经过 k
,要么经过 k
”。
用公式表示就是:
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
- 如果不经过
k
,则i
到j
的最短距离保持不变; - 如果经过
k
的路径更短(i→k→j
比直接i→j
短),则更新距离。
算法的执行过程
外层循环遍历所有可能的 中间节点 k
(从 1 到 n),内层循环遍历所有 起点 i
和 终点 j
:
- 初始时,
dist[i][j]
要么是直接连接i
和j
的道路长度,要么是无穷大(表示无直接道路)。 - 当引入第
k
个节点作为中间节点时,检查所有i
到j
的路径是否能通过k
优化。 - 随着
k
的增加,算法会逐步考虑所有可能的中间节点组合,最终得到全局最优的最短路径。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int N=1e5+10;int n,m,q;
ll dis[505][505];int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin>>n>>m>>q;for(int i=1;i<=n;++i){for(int j=1;j<=n;++j)dis[i][j]=LLONG_MAX/2;dis[i][i]=0;}for(int i=0;i<m;++i){ll a,b,c;cin>>a>>b>>c;if(c<dis[a][b])dis[a][b]=dis[b][a]=c;}for(int k=1;k<=n;++k)for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)if(dis[i][j]>dis[i][k]+dis[k][j])dis[i][j]=dis[i][k]+dis[k][j];while(q--){int a,b;cin>>a>>b;if(dis[a][b]==LLONG_MAX/2)cout<<"-1\n";else cout<<dis[a][b]<<'\n';}return 0;
}