题目地址:
https://www.acwing.com/problem/content/description/1170/
输入数据给出一个有
N
N
N个节点,
M
M
M条边的带权有向图。要求你写一个程序,判断这个有向图中是否存在负权回路。如果从一个点沿着某条路径出发,又回到了自己,而且所经过的边上的权和小于
0
0
0,就说这条路是一个负权回路。如果存在负权回路,只输出一行
−
1
−1
−1;如果不存在负权回路,再求出一个点
S
S
S到每个点的最短路的长度。约定:
S
S
S到
S
S
S的距离为
0
0
0,如果
S
S
S与这个点不连通, 则输出NoPath
。
输入格式:
第一行三个正整数,分别为点数
N
N
N,边数
M
M
M,源点
S
S
S;以下
M
M
M行,每行三个整数
a
,
b
,
c
a,b,c
a,b,c,表示点
a
,
b
a,b
a,b之间连有一条边,权值为
c
c
c。图中点的编号从
1
1
1到
N
N
N。
输出格式:
如果存在负权环,只输出一行
−
1
−1
−1,否则按以下格式输出:共
N
N
N行,第
i
i
i行描述
S
S
S点到点
i
i
i的最短路:如果
S
S
S与
i
i
i不连通,输出NoPath
;如果
i
=
S
i=S
i=S,输出
0
0
0。其他情况输出
S
S
S到
i
i
i的最短路的长度。
数据范围:
2
≤
N
≤
1000
2≤N≤1000
2≤N≤1000
1
≤
M
≤
1
0
5
1≤M≤10^5
1≤M≤105
1
≤
a
,
b
,
S
≤
N
1≤a,b,S≤N
1≤a,b,S≤N
∣
c
∣
≤
1
0
6
|c|≤10^6
∣c∣≤106
直接用SFPA来做即可。初始化的时候将距离数组 d d d的 d [ S ] d[S] d[S]赋值为 0 0 0,别的赋值为 + ∞ +\infty +∞,并且队列里将所有点都加进去。注意,无论 d d d怎么赋值,都不影响SPFA找负环。代码如下:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1010, M = 1e5 + 10, INF = 0x3f3f3f3f3f3f3f3f;
int n, m, S;
int h[N], e[M], ne[M], w[M], idx;
long dist[N];
int cnt[N];
bool st[N];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
// 求从S到每个点的最短路。如果发现负环则返回true,没有负环则返回false
bool spfa() {
memset(dist, 0x3f, sizeof dist);
dist[S] = 0;
queue<int> q;
for (int i = 1; i <= n; i++) {
q.push(i);
st[i] = true;
}
while (q.size()) {
int t = q.front(); q.pop();
st[t] = false;
for (int i = h[t]; ~i; i = ne[i]) {
int v = e[i];
if (dist[v] > dist[t] + w[i]) {
dist[v] = dist[t] + w[i];
cnt[v] = cnt[t] + 1;
if (cnt[v] >= n) return true;
if (!st[v]) {
q.push(v);
st[v] = false;
}
}
}
}
return false;
}
int main() {
cin >> n >> m >> S;
memset(h, -1, sizeof h);
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
if (spfa()) cout << -1 << endl;
else {
for (int i = 1; i <= n; i++)
if (dist[i] < INF) cout << dist[i] << endl;
else cout << "NoPath" << endl;
}
return 0;
}
时间复杂度 O ( m n ) O(mn) O(mn)(spfa判断负环的时间复杂度是比较高的),空间 O ( n ) O(n) O(n)。