diff --git a/TangDou/Topic/HDU1599.cpp b/TangDou/Topic/HDU1599.cpp index e5bddb3..30345d9 100644 --- a/TangDou/Topic/HDU1599.cpp +++ b/TangDou/Topic/HDU1599.cpp @@ -6,14 +6,16 @@ const int INF = 0x3f3f3f3f; const int N = 110; int dis[N][N], g[N][N]; -int n, m, a, b, c, ans; +int n, m, ans; void floyd() { + memcpy(dis, g, sizeof g); for (int k = 1; k <= n; k++) { - for (int i = 1; i < k; i++) // 枚举ij - for (int j = i + 1; j < k; j++) // 注意ijk不能相同 - if (ans > dis[i][j] + g[j][k] + g[k][i]) - ans = dis[i][j] + g[j][k] + g[k][i]; + // 最小环的DP操作 + for (int i = 1; i < k; i++) // 枚举i,j + for (int j = i + 1; j < k; j++) // 注意i,j,k不能相同 + if (ans > dis[i][j] + g[i][k] + g[k][j]) + ans = dis[i][j] + g[i][k] + g[k][j]; for (int i = 1; i <= n; i++) // 原floyd for (int j = 1; j <= n; j++) @@ -23,24 +25,24 @@ void floyd() { } signed main() { while (cin >> n >> m && (~n && ~m)) { + // 邻接矩阵初始化 for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (i == j) - dis[i][j] = g[i][j] = 0; + g[i][j] = 0; else - dis[i][j] = g[i][j] = INF; + g[i][j] = INF; + while (m--) { + int a, b, c; cin >> a >> b >> c; - if (c < dis[a][b]) // 防重边,怕了怕了 - dis[a][b] = g[a][b] = c; - if (c < dis[b][a]) - dis[b][a] = g[b][a] = c; + g[a][b] = g[b][a] = min(c, g[a][b]); // 防重边 } ans = INF; floyd(); if (ans == INF) puts("It's impossible."); else - printf("%lld\n", ans); + cout << ans << endl; } } \ No newline at end of file diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 457fec8..5ce48c2 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -5,14 +5,6 @@ $Floyd$算法是一次性求所有结点之间的最短距离,能处理负权 $Floyd$运用了 **动态规划** 的思想,求 $i 、 j$两点的最短距离,可分两种情况考虑,即经过图中某个点 $k$的路径和不经过点 $k$ 的路径,**取两者中的最短路径**。 -- 判断负圈 -眼尖的人儿可能发现邻接矩阵 $mp$ 中, $mp[i][i]$并没有赋初值$0$,而是 $inf$。并且计算后 $mp[i][i]$的值也不是 $0$,而是 $mp[i][i]=mp[i][u]+……+mp[v][i]$,即从外面绕一圈回来的最短路径,而这正 **用于判断负圈**,即 $mp[i][i]<0$。 - -相关变形结合题目讲,如:负圈、打印路径、最小环、传递闭包 - -记录坑点:**重复边**,保留最小的那个。 - - ### 二、模板 ```cpp {.line-numbers} void floyd() { @@ -27,6 +19,12 @@ void floyd() { ### 三、判负环 +眼尖的人儿可能发现邻接矩阵 $g$ 中, $g[i][i]$并没有赋初值$0$,而是 $inf$。并且计算后 $g[i][i]$的值也不是 $0$,而是 $g[i][i]=g[i][u]+……+g[v][i]$,即从外面绕一圈回来的最短路径,而这正 **用于判断负圈**,即 $g[i][i]<0$。 + +相关变形结合题目讲,如:负圈、打印路径、最小环、传递闭包 + +记录坑点:**重复边**,保留最小的那个。 + #### [$POJ-3259$ $Wormholes$](https://link.juejin.cn/?target=https%3A%2F%2Fvjudge.net%2Fproblem%2FPOJ-3259) **类型** @@ -218,7 +216,56 @@ It’s impossible 求最小环,用$dis[]$记录原距离,当枚举中间结点 $k$时,首先知道任意两点 $i、j$不经过 $k$的最短路径 $mp[i][j]$(原$floyd$的二三重循环后更新 $mp[i][j]$得到经过 $k$的最短路),此时枚举 $i$和 $j$得到一个经过 $k$的环( $i$到 $j$, $j$到 $k$, $k$到 $i$)并记录最小答案即可,即 $mp[i][j] + dis[j][k] + dis[k][i]$。 注意题目 $i, j, k$不能相同,还有坑点:`long long` +```cpp {.line-numbers} +#include +using namespace std; +#define int long long +#define endl "\n" +const int INF = 0x3f3f3f3f; +const int N = 110; +int dis[N][N], g[N][N]; +int n, m, ans; + +void floyd() { + memcpy(dis, g, sizeof g); + for (int k = 1; k <= n; k++) { + // 最小环的DP操作 + for (int i = 1; i < k; i++) // 枚举i,j + for (int j = i + 1; j < k; j++) // 注意i,j,k不能相同 + if (ans > dis[i][j] + g[i][k] + g[k][j]) + ans = dis[i][j] + g[i][k] + g[k][j]; + + for (int i = 1; i <= n; i++) // 原floyd + for (int j = 1; j <= n; j++) + if (dis[i][j] > dis[i][k] + dis[k][j]) + dis[i][j] = dis[i][k] + dis[k][j]; + } +} +signed main() { + while (cin >> n >> m && (~n && ~m)) { + // 邻接矩阵初始化 + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (i == j) + g[i][j] = 0; + else + g[i][j] = INF; + + while (m--) { + int a, b, c; + cin >> a >> b >> c; + g[a][b] = g[b][a] = min(c, g[a][b]); // 防重边 + } + ans = INF; + floyd(); + if (ans == INF) + puts("It's impossible."); + else + cout << ans << endl; + } +} +``` #### [$HDU$-$1704$ $Rank$](https://acm.hdu.edu.cn/showproblem.php?pid=1704) (传递闭包)