diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385.cpp new file mode 100644 index 0000000..99aa448 --- /dev/null +++ b/TangDou/Topic/HDU1385.cpp @@ -0,0 +1,56 @@ +#include +using namespace std; +const int INF = 0x3f3f3f3f; + +const int N = 1003; +int mp[N][N], path[N][N]; // 邻接矩阵、路径 +int n, x, y, u, v, cost[N]; // 额外费用 +void floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + if (mp[i][k] != INF) + for (int j = 1; j <= n; j++) { + if (mp[i][j] > mp[i][k] + mp[k][j] + cost[k]) { + mp[i][j] = mp[i][k] + mp[k][j] + cost[k]; + path[i][j] = path[i][k]; + } + if (mp[i][j] == mp[i][k] + mp[k][j] + cost[k]) { + 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; + cin >> mp[i][j]; + if (mp[i][j] == -1) mp[i][j] = INF; + } + } + for (int i = 1; i <= n; i++) cin >> cost[i]; + floyd(); + + while (cin >> x >> y) { + if (x == -1 && y == -1) break; + u = x, v = y; + printf("From %d to %d :\n", x, y); + printf("Path: %d", x); + + while (x != y) { + printf("-->%d", path[x][y]); + x = path[x][y]; + } + + printf("\n"); + + if (mp[u][v] < INF) + printf("Total cost : %d\n", mp[u][v]); + else + printf("-1\n"); + printf("\n"); + } + } + return 0; +} \ No newline at end of file diff --git a/TangDou/Topic/POJ3259.cpp b/TangDou/Topic/POJ3259.cpp index f2cd9a1..da42f28 100644 --- a/TangDou/Topic/POJ3259.cpp +++ b/TangDou/Topic/POJ3259.cpp @@ -9,14 +9,15 @@ const int N = 502; int n, m, w; int g[N][N]; +// floyd判断是否存在负圈 bool floyd() { for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) - if (g[i][k] != INF) { + if (g[i][k] != INF) { // 优化 for (int j = 1; j <= n; j++) if (g[i][j] > g[i][k] + g[k][j]) g[i][j] = g[i][k] + g[k][j]; - if (g[i][i] < 0) return true; + if (g[i][i] < 0) return true; // 发现负圈 } return false; } @@ -38,7 +39,7 @@ int main() { while (w--) { int a, b, c; cin >> a >> b >> c; - g[a][b] = -c; + g[a][b] = -c; // 负值边 } if (floyd()) diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 837022c..7a0502f 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -27,11 +27,88 @@ void floyd() { ### 三、例题 -#### [$POJ-3259$ $Wormholes$ 负圈](https://link.juejin.cn/?target=https%3A%2F%2Fvjudge.net%2Fproblem%2FPOJ-3259) +#### [$POJ-3259$ $Wormholes$](https://link.juejin.cn/?target=https%3A%2F%2Fvjudge.net%2Fproblem%2FPOJ-3259) + +**类型** +判负环 + +**题意** +- 正常路是$m$条双向正权边 +- 虫洞是$w$条单向负权边 +- 题目让判断是否有负权回路 + +**办法** +利用$Floyd$找两点间花费的最短时间,判断从起始位置到起始位置的最短时间是否为负值(判断负权环),若为负值,说明他通过虫洞回到起始位置时比自己最初离开起始位置的时间早。 + +**代码实现**: +在第二重循环,求完第$i$个结点后判断。$i$到$i$之间的最短距离是一个负值,说明存在一个经过它的负环。 + +```cpp {.line-numbers} +#include +#include +#include +#include +using namespace std; +const int INF = 0x3f3f3f3f; + +const int N = 502; +int n, m, w; +int g[N][N]; + +// floyd判断是否存在负圈 +bool floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + if (g[i][k] != INF) { // 优化 + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) + g[i][j] = g[i][k] + g[k][j]; + if (g[i][i] < 0) return true; // 发现负圈 + } + return false; +} +int main() { + int T; + cin >> T; + while (T--) { + cin >> n >> m >> w; + memset(g, INF, sizeof g); // 初始化邻接矩阵 + + // 双向正值边 + while (m--) { + int a, b, c; + cin >> a >> b >> c; + // 注意坑:重边 + g[a][b] = g[b][a] = min(c, g[a][b]); + } + // 单向负值边 + while (w--) { + int a, b, c; + cin >> a >> b >> c; + g[a][b] = -c; // 负值边 + } + + if (floyd()) + puts("YES"); + else + puts("NO"); + } + return 0; +} +``` + +#### [$HDU-1385$ $Minimum$ $Transport$ $Cost$](http://acm.hdu.edu.cn/showproblem.php?pid=1385) + +**类型** +打印路径 + +**题意** +给你所有城市到其他城市的道路成本和经过每个城市的城市税,给你很多组城市,要求你找出每组城市间的最低运输成本并且输出路径,如果有多条路径则输出字典序最小的那条路径。**注意**,起点城市和终点城市不需要收城市税。 + +**分析** +输出路径,多个答案则输出字典序最小的,无法到达输出$-1$。 +读入邻接表, $cost[]$记录每个城市额外费用, $path[][]$记录路径,比如 $path[i][j]=k$ 表示 $i$到 $j$的路径是 $i$先到 $k$,再从 $k$到 $j$,$floyd()$里维护即可。然后处理下输出(比较恶心)。 -**分析**: -给定若干双向正值边和单向负值边,问是否存在负圈(使其时光倒流回到原点)。 -所以在第二重循环,求完第$i$个结点后判断。