main
黄海 2 years ago
parent df29672268
commit 1e1d3fd916

@ -0,0 +1,56 @@
#include <bits/stdc++.h>
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;
}

@ -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())

@ -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 <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
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$个结点后判断。

Loading…
Cancel
Save