AcWing 852题中,spfa算法如何有效检测负环?

更新于
2026-10-10 09:44:15
2阅读来源:SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计666个文字,预计阅读时间需要3分钟。

AcWing 852题中,spfa算法如何有效检测负环?

题目:给定一个n个点m条边的有向图,图中可能存在重边和自环,边权可为负数。请判断图中是否存在负权回路。

输入格式:第一行包含两个整数n和m,分别表示点的个数和边的个数。接下来m行,每行包含三个整数a、b和c,表示从点a到点b的边,边权为c。

输出格式:如果图中存在负权回路,输出YES。如果图中不存在负权回路,输出NO。

题目

给定一个 $n$ 个点 $m$ 条边的有向图,图中可能存在重边和自环, 边权可能为负数。

请你判断图中是否存在负权回路。

输入格式 第一行包含整数 $n$ 和 $m$。

接下来 $m$ 行每行包含三个整数 $x,y,z$,表示存在一条从点 $x$ 到点 $y$ 的有向边,边长为 $z$。

输出格式 如果图中存在负权回路,则输出 Yes,否则输出 No。

数据范围 $1≤n≤2000,1≤m≤10000$,图中涉及边长绝对值均不超过 $10000$。

AcWing 852题中,spfa算法如何有效检测负环?

输入样例:

3 3 1 2 -1 2 3 4 3 1 -4

输出样例:

Yes

思路

基本思路,判断负环的话就增加一个数组 $cnt$ 记录某点最短距离需要的边数,每次更新时判断下 cnt ?>= n

queue <-- 1 存储变小的点,初始为1 while queue t = q.front q.pop 更新t的所有出边,并将出边端点加入队列

代码

#include <iostream> #include <cstring> #include <queue> using namespace std; const int N = 2010, M = 10010; int n, m; int h[N], e[M], w[M], ne[M], idx; int d[N], cnt[N]; bool st[N]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; } bool spfa() { memset(d, 0x3f, sizeof d); d[1] = 0; queue<int> q; for (int i = 1; i <= n; i ++ ) { q.push(i); st[i] = true; } while (q.size()) { auto t = q.front(); q.pop(); st[t] = false; for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; if (d[j] > d[t] + w[i]) { d[j] = d[t] + w[i]; cnt[j] = cnt[t] + 1; if (cnt[j] >= n) return true; // 判断点j的边数 if (!st[j]) { q.push(j); st[j] = true; } } } } return false; } int main() { scanf("%d%d", &n, &m); memset(h, -1, sizeof h); while (m -- ) { int a, b, c; scanf("%d%d%d", &a, &b, &c); add(a, b, c); } if (spfa()) puts("Yes"); else puts("No"); return 0; }

本文共计666个文字,预计阅读时间需要3分钟。

AcWing 852题中,spfa算法如何有效检测负环?

题目:给定一个n个点m条边的有向图,图中可能存在重边和自环,边权可为负数。请判断图中是否存在负权回路。

输入格式:第一行包含两个整数n和m,分别表示点的个数和边的个数。接下来m行,每行包含三个整数a、b和c,表示从点a到点b的边,边权为c。

输出格式:如果图中存在负权回路,输出YES。如果图中不存在负权回路,输出NO。

题目

给定一个 $n$ 个点 $m$ 条边的有向图,图中可能存在重边和自环, 边权可能为负数。

请你判断图中是否存在负权回路。

输入格式 第一行包含整数 $n$ 和 $m$。

接下来 $m$ 行每行包含三个整数 $x,y,z$,表示存在一条从点 $x$ 到点 $y$ 的有向边,边长为 $z$。

输出格式 如果图中存在负权回路,则输出 Yes,否则输出 No。

数据范围 $1≤n≤2000,1≤m≤10000$,图中涉及边长绝对值均不超过 $10000$。

AcWing 852题中,spfa算法如何有效检测负环?

输入样例:

3 3 1 2 -1 2 3 4 3 1 -4

输出样例:

Yes

思路

基本思路,判断负环的话就增加一个数组 $cnt$ 记录某点最短距离需要的边数,每次更新时判断下 cnt ?>= n

queue <-- 1 存储变小的点,初始为1 while queue t = q.front q.pop 更新t的所有出边,并将出边端点加入队列

代码

#include <iostream> #include <cstring> #include <queue> using namespace std; const int N = 2010, M = 10010; int n, m; int h[N], e[M], w[M], ne[M], idx; int d[N], cnt[N]; bool st[N]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; } bool spfa() { memset(d, 0x3f, sizeof d); d[1] = 0; queue<int> q; for (int i = 1; i <= n; i ++ ) { q.push(i); st[i] = true; } while (q.size()) { auto t = q.front(); q.pop(); st[t] = false; for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; if (d[j] > d[t] + w[i]) { d[j] = d[t] + w[i]; cnt[j] = cnt[t] + 1; if (cnt[j] >= n) return true; // 判断点j的边数 if (!st[j]) { q.push(j); st[j] = true; } } } } return false; } int main() { scanf("%d%d", &n, &m); memset(h, -1, sizeof h); while (m -- ) { int a, b, c; scanf("%d%d%d", &a, &b, &c); add(a, b, c); } if (spfa()) puts("Yes"); else puts("No"); return 0; }