3.1 最短路问题
地图
最 短 路 问 题 { 单 源 最 短 路 { 所 有 边 权 为 正 { 朴 素 D i j k s t r a 算 法 : 稠 密 图 ( m ∼ n 2 ) − O ( n 2 ) 堆 优 化 版 D i j k s t r a 算 法 : 稀 疏 图 ( m ∼ n ) − O ( m l o g n ) 存 在 负 权 边 { B e l l m a n − F o r d 算 法 : O ( n m ) S P F A 算 法 : 平 均 : O ( m ) , 最 坏 O ( n m ) 多 源 汇 最 短 路 F l o y d 算 法 : O ( n 3 ) 最短路问题\begin{cases}
单源最短路
\begin{cases}
所有边权为正
\begin{cases}
朴素Dijkstra算法:稠密图(m \sim n^2) - O(n^2)\\
\\
堆优化版Dijkstra算法:稀疏图(m \sim n) - O(mlogn)\\
\end{cases} \\
\\
存在负权边
\begin{cases}
Bellman-Ford算法:O(nm)\\
\\
SPFA算法:平均:O(m),最坏O(nm)\\
\end{cases} \\
\end{cases} \\
\\
\\
多源汇最短路 \ \ \ \ Floyd算法:O(n^3)\\
\\
\\
\end{cases}
最 短 路 问 题 ⎩ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎧ 单 源 最 短 路 ⎩ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎪ ⎧ 所 有 边 权 为 正 ⎩ ⎪ ⎨ ⎪ ⎧ 朴 素 D i j k s t r a 算 法 : 稠 密 图 ( m ∼ n 2 ) − O ( n 2 ) 堆 优 化 版 D i j k s t r a 算 法 : 稀 疏 图 ( m ∼ n ) − O ( m l o g n ) 存 在 负 权 边 ⎩ ⎪ ⎨ ⎪ ⎧ B e l l m a n − F o r d 算 法 : O ( n m ) S P F A 算 法 : 平 均 : O ( m ) , 最 坏 O ( n m ) 多 源 汇 最 短 路 F l o y d 算 法 : O ( n 3 )
注:$ n $ 为点数,$ m $ 为边数
3.1.1 朴素Dijkstra算法
思想
(0) $ dist[i] $ 为 $ 1 $ 号点到 $ i $ 号点的当前最短距离
集合S S S 存储当前已经确定有最短距离的点
(1) 初始化距离:$ dist[1] = 0, dist[i] = +\infty $
(2) $ for \ i : 0 \to n $
i.$\ t \ \leftarrow \ $ { 不在 S S S 中的,距离1 1 1 号点最近的点 }
ii.$\ S \ \leftarrow t \ $
iii. 用 $ t $ 更新其他点的距离
FAQ
(1) 存储方式:邻接矩阵
设邻接矩阵为$ g[N][N] $
$$
g[a][b] =
\begin{cases}
0 \ , 存在从a号点到b号点的有向边\\
1 \ , 不存在从a号点到b号点的有向边\\
\end{cases}
$$
(2) 初始化方式:
1 memset (a, 0x3f , sizeof a)
memset函数是以字节为单位进行赋值的
void *memset(void *s, int ch, size_t n);
将 $ s $ 所指向的某一块内存中的前 $ n $ 个字节的内容全部设置为 $ ch $ 指定的 $ ASCII $ 值
$ 0x3f = 0011 1111 = 63 $
C++中 $ int $ 型变量所占的位数为 $ 4 $ 个字节,即 $ 32 $ 位
$ 0x3f $ 显然不是 $ int $ 型变量中单个字节的最大值,应该是 $ 0x7f = 0111 1111 B $
那为什么要赋值 $ 0x3f $ :
a. 作为无穷大使用,因为 $ 4 $ 个字节均为 $ 0x3f $ 时,$ 0x3f3f3f3f $ 的十进制是 $ 1061109567 $ ,也就是 $ 10^ 9 $ 级别的(和 $ 0x7fffffff $ 一个数量级)
b. $ 0x3f3f3f3f + 0x3f3f3f3f = 2122219134 $ ,这非常大但却没有超过 $ 32-bit \ int $ 的表示范围,满足了“无穷大加无穷大还是无穷大”的需求。
(3) 如何处理重边和自环:任意两点之间的有向边取边权最小
代码
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 所 有 边 权 均 为 正 值 。 请 你 求 出 1 号 点 到 n 号 点 的 最 短 距 离 , 如 果 无 法 从 1 号 点 走 到 n 号 点 , 则 输 出 − 1 。 第 一 行 包 含 整 数 n 和 m 接 下 来 m 行 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。\\
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。\\
第一行包含整数 n 和 m \\
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。\\
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 所 有 边 权 均 为 正 值 。 请 你 求 出 1 号 点 到 n 号 点 的 最 短 距 离 , 如 果 无 法 从 1 号 点 走 到 n 号 点 , 则 输 出 − 1 。 第 一 行 包 含 整 数 n 和 m 接 下 来 m 行 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <iostream> #include <cstring> #include <algorithm> using namespace std;const int N = 510 ;int n, m;int g[N][N]; int dist[N]; bool st[N]; int dijkstra () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; for (int i = 0 ; i < n; i ++) { int t = -1 ; for (int j = 1 ; j <= n; j ++) { if (!st[j] && (t == -1 || dist[t] > dist[j])) { t = j; } } st[t] = true ; for (int j = 1 ; j <= n; j ++) { dist[j] = min (dist[j], dist[t] + g[t][j]); } } if (dist[n] == 0x3f3f3f3f ) return -1 ; return dist[n]; } int main () { scanf ("%d%d" , &n, &m); memset (g, 0x3f , sizeof g); while (m --) { int a, b, c; scanf ("%d%d%d" , &a, &b, &c); g[a][b] = min (g[a][b], c); } int t = dijkstra (); printf ("%d\n" , t); return 0 ; }
3.1.2 堆优化Dijkstra算法
代码
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 所 有 边 权 均 为 非 负 值 。 请 你 求 出 1 号 点 到 n 号 点 的 最 短 距 离 , 如 果 无 法 从 1 号 点 走 到 n 号 点 , 则 输 出 − 1 。 输 入 格 式 : 第 一 行 包 含 整 数 n 和 m 。 接 下 来 m 行 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。 输 出 格 式 : 输 出 一 个 整 数 , 表 示 1 号 点 到 n 号 点 的 最 短 距 离 。 如 果 路 径 不 存 在 , 则 输 出 − 1 。 数 据 范 围 : 1 ≤ n , m ≤ 1.5 × 1 0 5 图 中 涉 及 边 长 均 不 小 于 0 , 且 不 超 过 10000 。 数 据 保 证 : 如 果 最 短 路 存 在 , 则 最 短 路 的 长 度 不 超 过 109 。
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。\\
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。\\
输入格式:第一行包含整数 n 和 m。\\
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。\\
输出格式:输出一个整数,表示 1 号点到 n 号点的最短距离。如果路径不存在,则输出 −1。\\
数据范围:1≤n,m≤1.5×10^5 \\
图中涉及边长均不小于 0,且不超过 10000。数据保证:如果最短路存在,则最短路的长度不超过 109。\\
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 所 有 边 权 均 为 非 负 值 。 请 你 求 出 1 号 点 到 n 号 点 的 最 短 距 离 , 如 果 无 法 从 1 号 点 走 到 n 号 点 , 则 输 出 − 1 。 输 入 格 式 : 第 一 行 包 含 整 数 n 和 m 。 接 下 来 m 行 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。 输 出 格 式 : 输 出 一 个 整 数 , 表 示 1 号 点 到 n 号 点 的 最 短 距 离 。 如 果 路 径 不 存 在 , 则 输 出 − 1 。 数 据 范 围 : 1 ≤ n , m ≤ 1 . 5 × 1 0 5 图 中 涉 及 边 长 均 不 小 于 0 , 且 不 超 过 1 0 0 0 0 。 数 据 保 证 : 如 果 最 短 路 存 在 , 则 最 短 路 的 长 度 不 超 过 1 0 9 。
TODO:C++优先队列,邻接表,堆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include <iostream> #include <queue> #include <cstring> using namespace std;const int N = 1e6 ;int n, m; int h[N], e[N], w[N], ne[N], idx; int dist[N]; bool st[N]; typedef pair<int , int > PII; void add (int a, int b, int c) { e[idx] = b; w[idx] = c; ne[idx] = h[a]; h[a] = idx; idx ++; } int dijkstra () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; priority_queue<PII, vector<PII>, greater<PII>> heap; heap.push ({0 , 1 }); while (heap.size ()) { auto t = heap.top (); heap.pop (); int ver = t.second; int d = t.first; if (st[ver]) continue ; st[ver] = true ; for (int i = h[ver]; i != -1 ; i = ne[i]) { int j = e[i]; if (dist[j] > dist[ver] + w[i]) { dist[j] = dist[ver] + w[i]; heap.push ({dist[j], j}); } } } if (dist[n] == 0x3f3f3f3f ) return -1 ; return dist[n]; } int main () { scanf ("%d%d" , &n, &m); memset (h, -1 , sizeof h); while (m --) { int a, b, c; scanf ("%d%d%d" , &a, &b, &c); add (a, b, c); } cout << dijkstra () << endl; return 0 ; }
3.1.3 Bellman-Ford算法
代码
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 边 权 可 能 为 负 数 。 请 你 求 出 从 1 号 点 到 n 号 点 的 最 多 经 过 k 条 边 的 最 短 距 离 , 如 果 无 法 从 1 号 点 走 到 n 号 点 , 输 出 i m p o s s i b l e 。 注 意 : 图 中 可 能 存 在 负 权 回 路 。 输 入 格 式 : 第 一 行 包 含 三 个 整 数 n , m , k 。 接 下 来 m 行 , 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。 点 的 编 号 为 1 ∼ n 。 输 出 格 式 : 输 出 一 个 整 数 , 表 示 从 1 号 点 到 n 号 点 的 最 多 经 过 k 条 边 的 最 短 距 离 。 如 果 不 存 在 满 足 条 件 的 路 径 , 则 输 出 i m p o s s i b l e 。 数 据 范 围 1 ≤ n , k ≤ 500 , 1 ≤ m ≤ 10000 , 1 ≤ x , y ≤ n , 任 意 边 长 的 绝 对 值 不 超 过 10000 。
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。\\
请你求出从 1 号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出 impossible。\\
注意:图中可能 存在负权回路 。\\
输入格式:第一行包含三个整数 n,m,k。\\
接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。点的编号为 1∼n。\\
输出格式:输出一个整数,表示从 1 号点到 n 号点的最多经过 k 条边的最短距离。\\
如果不存在满足条件的路径,则输出 impossible。\\
数据范围1≤n,k≤500,1≤m≤10000,1≤x,y≤n,任意边长的绝对值不超过 10000。\\
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 边 权 可 能 为 负 数 。 请 你 求 出 从 1 号 点 到 n 号 点 的 最 多 经 过 k 条 边 的 最 短 距 离 , 如 果 无 法 从 1 号 点 走 到 n 号 点 , 输 出 i m p o s s i b l e 。 注 意 : 图 中 可 能 存 在 负 权 回 路 。 输 入 格 式 : 第 一 行 包 含 三 个 整 数 n , m , k 。 接 下 来 m 行 , 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。 点 的 编 号 为 1 ∼ n 。 输 出 格 式 : 输 出 一 个 整 数 , 表 示 从 1 号 点 到 n 号 点 的 最 多 经 过 k 条 边 的 最 短 距 离 。 如 果 不 存 在 满 足 条 件 的 路 径 , 则 输 出 i m p o s s i b l e 。 数 据 范 围 1 ≤ n , k ≤ 5 0 0 , 1 ≤ m ≤ 1 0 0 0 0 , 1 ≤ x , y ≤ n , 任 意 边 长 的 绝 对 值 不 超 过 1 0 0 0 0 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> #include <cstring> #include <algorithm> using namespace std;const int N = 510 , M = 100010 ;int n, m, k;int dist[N], backup[N];struct Edge { int a, b, w; }edges[M]; int bellman_ford () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; for (int i = 0 ; i < k ; i ++) { memcpy (backup, dist, sizeof dist); for (int j = 0 ; j < m; j ++) { int a = edges[j].a, b = edges[j].b, w = edges[j].w; if (dist[b] > backup[a] + w) dist[b] = backup[a] + w; } } if (dist[n] > 0x3f3f3f3f / 2 ) return -0x3f3f3f3f ; return dist[n]; } int main () { scanf ("%d%d%d" , &n, &m, &k); for (int i = 0 ; i < m; i ++) { int a, b, w; scanf ("%d%d%d" , &a, &b, &w); edges[i] = {a, b, w}; } int t = bellman_ford (); if (t == -0x3f3f3f3f ) puts ("impossible" ); else printf ("%d\n" , t); return 0 ; }
3.1.4 SPFA算法
代码
3.1.4.1 求最短路
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 边 权 可 能 为 负 数 。 请 你 求 出 1 号 点 到 n 号 点 的 最 短 距 离 , 如 果 无 法 从 1 号 点 走 到 n 号 点 , 则 输 出 i m p o s s i b l e 。 数 据 保 证 不 存 在 负 权 回 路 。 输 入 格 式 : 第 一 行 包 含 整 数 n 和 m 。 接 下 来 m 行 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。 输 出 格 式 : 输 出 一 个 整 数 , 表 示 1 号 点 到 n 号 点 的 最 短 距 离 。 如 果 路 径 不 存 在 , 则 输 出 i m p o s s i b l e 。 数 据 范 围 : 1 ≤ n , m ≤ 105 , 图 中 涉 及 边 长 绝 对 值 均 不 超 过 10000 。
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。\\
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 impossible。数据保证不存在负权回路。\\
输入格式:第一行包含整数 n 和 m。\\
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。\\
输出格式:输出一个整数,表示 1 号点到 n 号点的最短距离。如果路径不存在,则输出 impossible。\\
数据范围:1≤n,m≤105,图中涉及边长绝对值均不超过 10000。\\
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 边 权 可 能 为 负 数 。 请 你 求 出 1 号 点 到 n 号 点 的 最 短 距 离 , 如 果 无 法 从 1 号 点 走 到 n 号 点 , 则 输 出 i m p o s s i b l e 。 数 据 保 证 不 存 在 负 权 回 路 。 输 入 格 式 : 第 一 行 包 含 整 数 n 和 m 。 接 下 来 m 行 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。 输 出 格 式 : 输 出 一 个 整 数 , 表 示 1 号 点 到 n 号 点 的 最 短 距 离 。 如 果 路 径 不 存 在 , 则 输 出 i m p o s s i b l e 。 数 据 范 围 : 1 ≤ n , m ≤ 1 0 5 , 图 中 涉 及 边 长 绝 对 值 均 不 超 过 1 0 0 0 0 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <cstring> #include <iostream> #include <queue> using namespace std;const int N = 100010 , M = 100010 ;int n, m;int h[N], e[N], w[N], ne[N], idx;int dist[N];bool st[N];void add (int x, int y, int z) { e[idx] = y; w[idx] = z; ne[idx] = h[x]; h[x] = idx ++; } int spfa () { memset (dist, 0x3f , sizeof dist); dist[1 ] = 0 ; queue<int > q; q.push (1 ); st[1 ] = true ; while (q.size ()) { int t = q.front (); q.pop (); st[t] = false ; for (int i = h[t]; i != -1 ; i = ne[i]) { int j = e[i]; if (dist[j] > dist[t] + w[i]) { dist[j] = dist[t] + w[i]; if (!st[j]) { q.push (j); st[j] = true ; } } } } if (dist[n] == 0x3f3f3f3f ) return -0x3f3f3f3f ; else return dist[n]; } int main () { scanf ("%d%d" , &n, &m); memset (h, -1 , sizeof h); while (m --) { int x, y, z; scanf ("%d%d%d" , &x, &y, &z); add (x, y, z); } int t = spfa (); if (t == -0x3f3f3f3f ) puts ("impossible" ); else printf ("%d\n" , t); }
3.1.4.2 判断负环
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 边 权 可 能 为 负 数 。 判 断 图 中 是 否 存 在 负 权 回 路 。 输 入 格 式 : 第 一 行 包 含 整 数 n 和 m 。 接 下 来 m 行 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。 输 出 格 式 : 如 果 图 中 存 在 负 权 回 路 , 则 输 出 Y e s , 否 则 输 出 N o 。 数 据 范 围 : 1 ≤ n ≤ 2000 , 1 ≤ m ≤ 10000 , 图 中 涉 及 边 长 绝 对 值 均 不 超 过 10000 。
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。判断图中是否存在负权回路。\\
输入格式:第一行包含整数 n 和 m。\\
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。\\
输出格式:如果图中存在负权回路,则输出 Yes,否则输出 No。\\
数据范围:1≤n≤2000,1≤m≤10000,图中涉及边长绝对值均不超过 10000。\\
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 边 权 可 能 为 负 数 。 判 断 图 中 是 否 存 在 负 权 回 路 。 输 入 格 式 : 第 一 行 包 含 整 数 n 和 m 。 接 下 来 m 行 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。 输 出 格 式 : 如 果 图 中 存 在 负 权 回 路 , 则 输 出 Y e s , 否 则 输 出 N o 。 数 据 范 围 : 1 ≤ n ≤ 2 0 0 0 , 1 ≤ m ≤ 1 0 0 0 0 , 图 中 涉 及 边 长 绝 对 值 均 不 超 过 1 0 0 0 0 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include <cstring> #include <iostream> #include <queue> using namespace std;const int N = 100010 , M = 100010 ;int n, m;int h[N], e[N], w[N], ne[N], idx;int dist[N], cnt[N]; bool st[N];void add (int x, int y, int z) { e[idx] = y; w[idx] = z; ne[idx] = h[x]; h[x] = idx ++; } bool spfa () { queue<int > q; for (int i = 1 ; i <= n; i ++) { q.push (i); st[i] = true ; } while (q.size ()) { int t = q.front (); q.pop (); st[t] = false ; for (int i = h[t]; i != -1 ; i = ne[i]) { int j = e[i]; if (dist[j] > dist[t] + w[i]) { dist[j] = dist[t] + w[i]; cnt[j] = cnt[t] + 1 ; if (cnt[j] >= n) return true ; if (!st[j]) { q.push (j); st[j] = true ; } } } } return false ; } int main () { scanf ("%d%d" , &n, &m); memset (h, -1 , sizeof h); while (m --) { int x, y, z; scanf ("%d%d%d" , &x, &y, &z); add (x, y, z); } if (spfa ()) puts ("Yes" ); else puts ("No" ); return 0 ; }
3.1.5 Floyd算法
代码
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 边 权 可 能 为 负 数 。 再 给 定 k 个 询 问 , 每 个 询 问 包 含 两 个 整 数 x 和 y , 表 示 查 询 从 点 x 到 点 y 的 最 短 距 离 。 如 果 路 径 不 存 在 , 则 输 出 i m p o s s i b l e 。 数 据 保 证 图 中 不 存 在 负 权 回 路 。 输 入 格 式 : 第 一 行 包 含 三 个 整 数 n , m , k 。 接 下 来 m 行 , 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。 接 下 来 k 行 , 每 行 包 含 两 个 整 数 x , y , 表 示 询 问 点 x 到 点 y 的 最 短 距 离 。 输 出 格 式 : 共 k 行 , 每 行 输 出 一 个 整 数 , 表 示 询 问 的 结 果 , 若 询 问 两 点 间 不 存 在 路 径 , 则 输 出 i m p o s s i b l e 。 数 据 范 围 ; 1 ≤ n ≤ 200 , 1 ≤ k ≤ n 2 , 1 ≤ m ≤ 20000 , 图 中 涉 及 边 长 绝 对 值 均 不 超 过 10000 。
给定一个 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。\\
给 定 一 个 n 个 点 m 条 边 的 有 向 图 , 图 中 可 能 存 在 重 边 和 自 环 , 边 权 可 能 为 负 数 。 再 给 定 k 个 询 问 , 每 个 询 问 包 含 两 个 整 数 x 和 y , 表 示 查 询 从 点 x 到 点 y 的 最 短 距 离 。 如 果 路 径 不 存 在 , 则 输 出 i m p o s s i b l e 。 数 据 保 证 图 中 不 存 在 负 权 回 路 。 输 入 格 式 : 第 一 行 包 含 三 个 整 数 n , m , k 。 接 下 来 m 行 , 每 行 包 含 三 个 整 数 x , y , z , 表 示 存 在 一 条 从 点 x 到 点 y 的 有 向 边 , 边 长 为 z 。 接 下 来 k 行 , 每 行 包 含 两 个 整 数 x , y , 表 示 询 问 点 x 到 点 y 的 最 短 距 离 。 输 出 格 式 : 共 k 行 , 每 行 输 出 一 个 整 数 , 表 示 询 问 的 结 果 , 若 询 问 两 点 间 不 存 在 路 径 , 则 输 出 i m p o s s i b l e 。 数 据 范 围 ; 1 ≤ n ≤ 2 0 0 , 1 ≤ k ≤ n 2 , 1 ≤ m ≤ 2 0 0 0 0 , 图 中 涉 及 边 长 绝 对 值 均 不 超 过 1 0 0 0 0 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> #include <cstring> #include <algorithm> using namespace std;const int N = 210 , INF = 1e9 ;int d[N][N];int n, m, Q;void floyd () { 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]); } } } } 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) d[i][j] = 0 ; else d[i][j] = INF; } } while (m --) { int a, b, c; scanf ("%d%d%d" , &a, &b, &c); d[a][b] = min (d[a][b], c); } floyd (); while (Q --) { int a, b; scanf ("%d%d" , &a, &b); if (d[a][b] > INF / 2 ) puts ("impossible" ); else printf ("%d\n" , d[a][b]); } }