因为Floyd算法太简单了,所以和数轮简介放在一起。
Floyd算法是求多元最短路的利器。
就是时间复杂度吗……
emm, O ( n 3 ) O({n} ^ 3) O(n3)……
先说一下什么是多元最短路:
(划重点了!!!
的确,Floyd算法就是基于 动 态 规 划 动态规划 动态规划实现滴!
既然是DP,那么我们就需要搬出思考DP最nb的思考方式:
F l o y d : { 状 态 表 示 : { 集 合 — — 所 有 集 合 属 性 : M i n 因 为 是 最 短 路 , 所 以 是 M i n d i s t [ i ] [ j ] [ k ] { i : 从 第 i 个 点 j : 到 第 j 个 点 k : 指 经 过 1 − k 状 态 计 算 : { d i s t [ k , i , j ] : 计 算 目 标 , 从 i 走 到 j 经 过 1 − k 的 最 短 路 。 更 新 : { 从 k − 1 转 移 : d i s t [ k − 1 , i , j ] : 指 经 过 1 到 k − 1 加 上 : d i s t [ k − 1 ] [ k ] [ j ] : 指 经 过 k − 1 条 边 从 k 走 到 j 。 Floyd: \left\{ \begin{aligned} 状态表示: & \left\{ \begin{aligned} & 集合——所有 & \\ \\ \\ & 集合属性:Min因为是最短路,所以是Min \\ \\ \\ & dist[i][j][k] \left \{ \begin{aligned} i: &从第i个点 \\ j: &到第j个点 \\ k: &指经过1-k \\ \end{aligned} \right. & \end{aligned} \right. \\ \\ \\ \\ 状态计算:& \left\{ \begin{aligned} & dist[k, i, j]:计算目标,从i走到j经过1-k的最短路。 \\ \\ \\ & 更新: \left\{ \begin{aligned} & 从k-1转移:dist[k-1,i,j]:指经过1到k-1 \\ \\ & 加上: \\ \\ & dist[k-1][k][j]:指经过k-1条边从k走到j。 \\ \end{aligned} \right. \end{aligned} \right. \end{aligned} \right. Floyd:⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧状态表示:状态计算:⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧集合——所有集合属性:Min因为是最短路,所以是Mindist[i][j][k]⎩⎪⎨⎪⎧i:j:k:从第i个点到第j个点指经过1−k⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧dist[k,i,j]:计算目标,从i走到j经过1−k的最短路。更新:⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧从k−1转移:dist[k−1,i,j]:指经过1到k−1加上:dist[k−1][k][j]:指经过k−1条边从k走到j。
我们可以通过类似于后面要讲的滚动数组优化01背包问题的方法优化Floyd。
优化后,Floyd算法的状态转移方程是:
d i s t [ i ] [ j ] = m i n ( d i s t [ i ] [ j ] , d i s t [ i ] [ k ] + d i s t [ k ] [ j ] ) ; dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]); dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
然后来说一下如何一步一步的推出来 d i s t [ i ] [ j ] dist[i][j] dist[i][j]
我们可以直接进行三层循环,分别循环k,i,j,每一次搞一下状态转移方程就好了。
为了方便,我们把dist改成了d。
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]);
完了?
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≤n^2\
1≤m≤20000,\
$
图中涉及边长绝对值均不超过10000。
输入样例:
3 3 2
1 2 1
2 3 2
1 3 1
2 1
1 3
输出样例:
impossible
1
我们这里加上一些Floyd算法的初始化与判断最短路是否无解。
初始化:
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
if(i == j) d[i][j] = 0;//自己到自己的最短路为0
else d[i][j] = 1e9;
然后读入时我们就把d当成邻接矩阵就好了。
while(m --)
{
int a, b, c;
cin >> a >> b >> c;
d[a][b] = min(d[a][b], c);
}
判断是否无解不能直接用0x3f3f3f3f
,
if(d[a][b] > 1e9 / 2) puts("impossible");
所以这里改成了这样。
完整代码:
#include
using namespace std;
const int N = 210, X = 1e9;
int n, m, q, d[N][N];
int main()
{
cin >> n >> m >> q;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
if(i == j) d[i][j] = 0;
else d[i][j] = X;
while(m --)
{
int a, b, c;
cin >> a >> b >> c;
d[a][b] = min(d[a][b], c);
}
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]);
while(q --)
{
int a, b;
cin >> a >> b;
if(d[a][b] > X / 2) puts("impossible");
else cout << d[a][b] << endl;
}
}
作者:cht
链接:https://www.acwing.com/activity/content/code/content/336003/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
众所周知,各位大学生们拥有3打苦处:
{ 离 散 数 学 — — 一 些 自 闭 理 论 与 证 明 组 合 数 学 — — 更 多 自 闭 理 论 与 证 明 高 等 数 学 — — 新 的 自 闭 理 论 与 证 明 \left\{ \begin{aligned} 离散数学——一些自闭理论与证明 \\ 组合数学——更多自闭理论与证明\\ 高等数学——新的自闭理论与证明\\ \end{aligned} \right. ⎩⎪⎨⎪⎧离散数学——一些自闭理论与证明组合数学——更多自闭理论与证明高等数学——新的自闭理论与证明
所以数论由此诞生!
我们会讲解如下这些内容:
(不会附链接!请大家实时关注!!!
快速幂以前已经讲过啦!!!!
这些课时的顺序可能会改变!!!