From 5e156dfb916ba7a417fdac12f4edb986a0f33c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 08:59:01 +0800 Subject: [PATCH] 'commit' --- TangDou/Topic/HDU1385.in | 12 +++ TangDou/Topic/HDU1385_Precursor.cpp | 71 ++++++++++++++++ .../{HDU1385.cpp => HDU1385_Subsequent.cpp} | 15 ++++ TangDou/Topic/【Floyd专题】.md | 80 ++++++++----------- 4 files changed, 133 insertions(+), 45 deletions(-) create mode 100644 TangDou/Topic/HDU1385.in create mode 100644 TangDou/Topic/HDU1385_Precursor.cpp rename TangDou/Topic/{HDU1385.cpp => HDU1385_Subsequent.cpp} (85%) diff --git a/TangDou/Topic/HDU1385.in b/TangDou/Topic/HDU1385.in new file mode 100644 index 0000000..722ece9 --- /dev/null +++ b/TangDou/Topic/HDU1385.in @@ -0,0 +1,12 @@ +5 +0 3 22 -1 4 +3 0 5 -1 -1 +22 5 0 9 20 +-1 -1 9 0 4 +4 -1 20 4 0 +5 17 8 3 1 +1 3 +3 5 +2 4 +-1 -1 +0 \ No newline at end of file diff --git a/TangDou/Topic/HDU1385_Precursor.cpp b/TangDou/Topic/HDU1385_Precursor.cpp new file mode 100644 index 0000000..3c4d321 --- /dev/null +++ b/TangDou/Topic/HDU1385_Precursor.cpp @@ -0,0 +1,71 @@ +#include +using namespace std; + +const int N = 110; +const int INF = 0x3f3f3f3f; +// Floyd+记录终点前驱 +int n; +int g[N][N], w[N]; +int path[N][N]; // 记录i到j最短路径中j的前驱 + +void floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) { + if (g[i][j] > g[i][k] + g[k][j] + w[k]) { + g[i][j] = g[i][k] + g[k][j] + w[k]; + path[i][j] = path[i][k]; // i->j这条最短路径上,i后面第一个节点,是i->k路径上第一个节点 + } + // 相同路径下选择后继更小的(为了字典序) + if (g[i][j] == g[i][k] + g[k][j] + w[k]) + if (path[i][j] > path[i][k]) + path[i][j] = path[i][k]; + } +} + +// 递归输出路径 +void print(int s, int e) { + printf("-->%d", path[s][e]); // 输出s的后继 + if (path[s][e] != e) // 如果不是直连 + print(path[s][e], e); // 递归输出后继 +} + +/* +From 1 to 3 : +Path: 1-->5-->4-->3 +Total cost : 21 + +From 3 to 5 : +Path: 3-->4-->5 +Total cost : 16 + +From 2 to 4 : +Path: 2-->1-->5-->4 +Total cost : 17 +*/ +int main() { +#ifndef ONLINE_JUDGE + freopen("HDU1385.in", "r", stdin); +#endif + while (cin >> n, n) { + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) { + cin >> g[i][j]; + if (g[i][j] == -1) g[i][j] = INF; + path[i][j] = j; + } + + for (int i = 1; i <= n; i++) cin >> w[i]; + floyd(); + + int s, e; + + while (cin >> s >> e, ~s && ~e) { + printf("From %d to %d :\n", s, e); + printf("Path: %d", s); + if (s != e) print(s, e); // 起点与终点不同开始递归 + printf("\nTotal cost : %d\n\n", g[s][e]); + } + } + return 0; +} \ No newline at end of file diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385_Subsequent.cpp similarity index 85% rename from TangDou/Topic/HDU1385.cpp rename to TangDou/Topic/HDU1385_Subsequent.cpp index c04c083..9f519e4 100644 --- a/TangDou/Topic/HDU1385.cpp +++ b/TangDou/Topic/HDU1385_Subsequent.cpp @@ -29,8 +29,23 @@ void print(int s, int e) { if (path[s][e] != e) // 如果不是直连 print(path[s][e], e); // 递归输出后继 } +/* +From 1 to 3 : +Path: 1-->5-->4-->3 +Total cost : 21 +From 3 to 5 : +Path: 3-->4-->5 +Total cost : 16 + +From 2 to 4 : +Path: 2-->1-->5-->4 +Total cost : 17 +*/ int main() { +#ifndef ONLINE_JUDGE + freopen("HDU1385.in", "r", stdin); +#endif while (cin >> n, n) { for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index c9a9199..10e5f57 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -227,65 +227,55 @@ $i \rightarrow a \rightarrow b \rightarrow c \rightarrow d \rightarrow j$,则$pa ```cpp {.line-numbers} #include using namespace std; + +const int N = 110; const int INF = 0x3f3f3f3f; +// Floyd+记录起点后继 +int n; +int g[N][N], w[N]; +int path[N][N]; // 记录i到j最短路径中i的后继 -const int N = 1003; -int g[N][N]; // 邻接矩阵 -int n; // n个点 -int w[N]; // 额外费用 -int path[N][N]; // i->j 可能存在多条路线,我要找最短的。如果有多条最短的,我要字典序最小的。现在路线唯一了吧!比如这条路线最终是 -// i->a->b->c->d->j,则path[i][j]=a,也就是第一个后继节点。 void floyd() { for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) - if (g[i][k] != INF) // floyd优化 - for (int j = 1; j <= n; j++) { - if (g[i][j] > g[i][k] + g[k][j] + w[k]) { // w[k]:点权 - g[i][j] = g[i][k] + g[k][j] + w[k]; // k的加入,使得i->j的路径变短 - path[i][j] = path[i][k]; // 如果i->k->j使得i->j更近,那么根据定义path[i][j]就是这条最短路径中距离i最近的那个点,而这个点由于是出现在i->k的必经之路上,而且是i->k的首席弟子,所以,也必然是i->j的首席弟子。 - } - // 处理字典序 - if (g[i][j] == g[i][k] + g[k][j] + w[k]) { // 如果存在多条最短路径,也就是,除了k还有其它k1,k2使得i->j距离一样小 - if (path[i][j] > path[i][k]) path[i][j] = path[i][k]; // 字典序,谁更小就留下谁 - } + for (int j = 1; j <= n; j++) { + if (g[i][j] > g[i][k] + g[k][j] + w[k]) { + g[i][j] = g[i][k] + g[k][j] + w[k]; + path[i][j] = path[i][k]; // i->j这条最短路径上,i后面第一个节点,是i->k路径上第一个节点 } + // 相同路径下选择后继更小的(为了字典序) + if (g[i][j] == g[i][k] + g[k][j] + w[k]) + if (path[i][j] > path[i][k]) + path[i][j] = path[i][k]; + } +} + +// 递归输出路径 +void print(int s, int e) { + printf("-->%d", path[s][e]); // 输出s的后继 + if (path[s][e] != e) // 如果不是直连 + print(path[s][e], e); // 递归输出后继 } + int main() { - while (cin >> n && n) { - for (int i = 1; i <= n; i++) { + while (cin >> n, n) { + for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { - path[i][j] = j; // 路径初始化,记录整条路径上,离i节点最近的,最短路径上的下一个点,只有i->j时,下一个点可不就是j - cin >> g[i][j]; // 不管是不是有边,都先录进来 - if (g[i][j] == -1) g[i][j] = INF; // 如果题目中给出的是无边,那么设置为正无穷。此时,有些记录的path[i][j]就是没用的,但没事,后面会被其它代码替换掉path[i][j]。 + cin >> g[i][j]; + if (g[i][j] == -1) g[i][j] = INF; + path[i][j] = j; } - } - for (int i = 1; i <= n; i++) cin >> w[i]; // 读入点权 - // 多源最短路径 + for (int i = 1; i <= n; i++) cin >> w[i]; floyd(); - // 处理询问 - int x, y; - while (cin >> x >> y) { - if (x == -1 && y == -1) break; - printf("From %d to %d :\n", x, y); - printf("Path: %d", x); - int u = x, v = y; - // 理解路径思路: - // (1) 从起点x出发,用循环打印路径,最后一个打印的肯定是y - // (2) 从起点x出发,第二个点应该是离x最近的,并且是最短路径上的那个点,这个点就是path[x][y]! - // path[x][y]:从起点x出发,到终点y有多条最短路径,我们选择字典序最小的那条最短路径,然后path[x][y]就是从x出发,离x最近的这条最短路径上的点。 - while (x != y) { - printf("-->%d", path[x][y]); // 输出距离x最近的那个点 - x = path[x][y]; // 更换x概念,向y逼近,让循环跑起来 - } + int s, e; - puts(""); - if (g[u][v] < INF) - printf("Total cost : %d\n", g[u][v]); - else - puts("-1"); - puts(""); + while (cin >> s >> e, ~s && ~e) { + printf("From %d to %d :\n", s, e); + printf("Path: %d", s); + if (s != e) print(s, e); // 起点与终点不同开始递归 + printf("\nTotal cost : %d\n\n", g[s][e]); } } return 0;