diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385.cpp index 94c12d8..b0440f2 100644 --- a/TangDou/Topic/HDU1385.cpp +++ b/TangDou/Topic/HDU1385.cpp @@ -6,7 +6,8 @@ const int N = 1003; int g[N][N]; // 邻接矩阵 int n; // n个点 int w[N]; // 额外费用 -int path[N][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++) @@ -14,9 +15,9 @@ void 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]; + 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]; // 字典序,谁更小就留下谁 } @@ -26,12 +27,13 @@ int main() { 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] = j; // 路径初始化,记录整条路径上,离i节点最近的,最短路径上的下一个点,只有i->j时,下一个点可不就是j + cin >> g[i][j]; // 不管是不是有边,都先录进来 + if (g[i][j] == -1) g[i][j] = INF; // 如果题目中给出的是无边,那么设置为正无穷。此时,有些记录的path[i][j]就是没用的,但没事,后面会被其它代码替换掉path[i][j]。 } } for (int i = 1; i <= n; i++) cin >> w[i]; // 读入点权 + // 多源最短路径 floyd(); diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 7a0502f..3b0b46b 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -103,13 +103,83 @@ int main() { 打印路径 **题意** -给你所有城市到其他城市的道路成本和经过每个城市的城市税,给你很多组城市,要求你找出每组城市间的最低运输成本并且输出路径,如果有多条路径则输出字典序最小的那条路径。**注意**,起点城市和终点城市不需要收城市税。 +给你所有城市到其他城市的道路成本和经过每个城市的城市税,给你很多组城市,要求你找出每组城市间的最低运输成本并且输出路径,**如果有多条路径则输出字典序最小的那条路径**。 **注意**,起点城市和终点城市不需要收城市税(中间点才收税,也就是插值的$k$收税)。 **分析** 输出路径,多个答案则输出字典序最小的,无法到达输出$-1$。 -读入邻接表, $cost[]$记录每个城市额外费用, $path[][]$记录路径,比如 $path[i][j]=k$ 表示 $i$到 $j$的路径是 $i$先到 $k$,再从 $k$到 $j$,$floyd()$里维护即可。然后处理下输出(比较恶心)。 +读入邻接表, $w[]$记录每个城市额外费用, $path[][]$记录路径,$floyd()$里维护即可。然后处理下输出(比较恶心)。 +> **解释**:`int path[N][N]; ` +$i \rightarrow j$ 可能存在多条路线,我要找最短的。如果有多条最短的,我要字典序最小的。现在路线唯一了吧!比如这条路线最终是 +$i \rightarrow a \rightarrow b \rightarrow c \rightarrow d \rightarrow j$,则$path[i][j]=a$,也就是第一个后继节点。 +```cpp {.line-numbers} +#include +using namespace std; +const int INF = 0x3f3f3f3f; + +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]; // 字典序,谁更小就留下谁 + } + } +} +int main() { + 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]。 + } + } + 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逼近,让循环跑起来 + } + + puts(""); + if (g[u][v] < INF) + printf("Total cost : %d\n", g[u][v]); + else + puts("-1"); + puts(""); + } + } + return 0; +} +``` https://juejin.cn/post/6935691567696969764 \ No newline at end of file