当前位置: 首页 > news >正文

题解:洛谷 P3385 【模板】负环

【题目来源】

洛谷:P3385 【模板】负环 - 洛谷

【题目描述】

给定一个 \(n\) 个点的有向图,请求出图中是否存在从顶点 \(1\) 出发能到达的负环。

负环的定义是:一条边权之和为负数的回路。

【输入】

输入的第一行是一个整数 \(T\),表示测试数据的组数。对于每组数据的格式如下:

第一行有两个整数,分别表示图的点数 \(n\) 和接下来给出边信息的条数 \(m\)

接下来 \(m\) 行,每行三个整数 \(u,v,w\)

  • \(w≥0\),则表示存在一条从 \(u\)\(v\) 边权为 \(w\) 的边,还存在一条从 \(v\)\(u\) 边权为 \(w\) 的边。
  • \(w<0\),则只表示存在一条从 \(u\)\(v\) 边权为 \(w\) 的边。

【输出】

对于每组数据,输出一行一个字符串,若所求负环存在,则输出 YES,否则输出 NO

【输入样例】

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

【输出样例】

NO
YES

【算法标签】

《洛谷 P3385 负环》 #模板题# #O2优化#

【代码详解】

//Ford 判负环 740ms
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstring>  // 需要包含memset
using namespace std;const int inf = 0x3f3f3f3f;  // 定义无穷大
const int N = 2010;          // 最大顶点数
const int M = 6010;          // 最大边数
int n, m;                    // 顶点数,边数
int to[M], ne[M], w[M], h[N], tot;  // 链式前向星
int d[N];                  // 最短距离数组/*** 添加有向边* @param a 起点* @param b 终点* @param c 权重*/
void add(int a, int b, int c)
{to[++tot] = b;   // 边指向的顶点w[tot] = c;      // 边的权重ne[tot] = h[a];  // 指向原链表头h[a] = tot;      // 更新头指针
}/*** Bellman-Ford算法实现,检测负权环* 原理:如果没有负权环,最多n-1轮松弛可得到最短路径* 如果第n轮还能松弛,说明存在负权环* @return 存在负权环返回true,否则返回false*/
bool ford()
{memset(d, inf, sizeof d);  // 初始化距离为无穷大d[1] = 0;  // 假设起点为1,距离为0bool flag;  // 标记本轮是否进行了松弛操作// 最多进行n轮松弛for (int i = 1; i <= n; i++)  // 跑n轮{flag = false;  // 初始化为未松弛// 遍历所有顶点for (int u = 1; u <= n; u++)  // n个点{if (d[u] == inf)  // 如果当前顶点不可达,跳过{continue;}// 遍历u的所有出边for (int j = h[u]; j; j = ne[j]){int v = to[j];  // 邻接顶点// 松弛操作if (d[v] > d[u] + w[j]){d[v] = d[u] + w[j];flag = true;  // 标记有松弛}}}// 如果本轮没有松弛,提前结束if (!flag){break;}}// 如果第n轮还能松弛,说明存在负权环return flag;  // 第n轮=true,有负环
}int main()
{int T;  // 测试用例数量scanf("%d", &T);while (T--){// 初始化邻接表tot = 0;memset(h, 0, sizeof(h));// 输入顶点数和边数scanf("%d%d", &n, &m);// 读入边for (int i = 1; i <= m; i++){int u, v, w;scanf("%d%d%d", &u, &v, &w);add(u, v, w);  // 添加有向边// 如果是非负边,添加反向边(构成无向边)if (w >= 0){add(v, u, w);}}// 检测负权环并输出结果puts(ford() ? "YES" : "NO");}return 0;
}
// 使用acwing模板二刷
#include <bits/stdc++.h>
using namespace std;const int N = 2005;   // 最大顶点数
const int M = 6005;   // 最大边数int h[N], e[M], w[M], ne[M], idx;  // 链式前向星存储图
int dist[N];    // 最短距离数组
int cnt[N];     // 记录每个顶点被松弛的次数
bool st[N];     // 标记顶点是否在队列中
int T, n, m;    // T: 测试用例数, n: 顶点数, m: 边数/*** 添加有向边* @param a 起点* @param b 终点* @param c 权重*/
void add(int a, int b, int c)
{e[idx] = b;        // 边指向的顶点w[idx] = c;        // 边的权重ne[idx] = h[a];    // 指向原链表头h[a] = idx++;      // 更新头指针
}/*** SPFA算法检测负权环* 从所有顶点开始检测,确保能找到任意连通分量中的负权环* @return 存在负权环返回true,否则返回false*/
int spfa()
{queue<int> q;// 初始化memset(dist, 0, sizeof(dist));  // 距离初始化为0memset(cnt, 0, sizeof(cnt));    // 松弛次数初始化为0// 将所有顶点入队for (int i = 1; i <= n; i++){st[i] = true;  // 标记在队列中q.push(i);     // 顶点入队}// SPFA主循环while (q.size()){int t = q.front();  // 取出队首顶点q.pop();st[t] = false;      // 标记不在队列中// 遍历t的所有邻接边for (int i = h[t]; i != -1; i = ne[i]){int j = e[i];  // 邻接顶点// 松弛操作if (dist[j] > dist[t] + w[i]){dist[j] = dist[t] + w[i];  // 更新最短距离cnt[j] = cnt[t] + 1;       // 松弛次数+1// 如果顶点j被松弛了n次,说明存在负权环if (cnt[j] >= n){return true;  // 存在负权环}// 如果j不在队列中,入队if (!st[j]){q.push(j);st[j] = true;}}}}return false;  // 不存在负权环
}int main()
{// 输入测试用例数量cin >> T;while (T--){// 输入顶点数和边数cin >> n >> m;// 初始化邻接表memset(h, -1, sizeof(h));idx = 0;  // 重置边索引// 读入边for (int i = 1; i <= m; i++){int a, b, c;cin >> a >> b >> c;add(a, b, c);  // 添加有向边// 如果是非负边,添加反向边(构成无向边)if (c >= 0){add(b, a, c);}}/*** 判断是否存在从顶点1出发可达的负权环* 条件:* 1. 存在负权环 (!spfa() 返回false时表示有负环)* 2. 顶点1必须有出边 (h[1] != -1)* 注意:spfa()返回true表示有负环,false表示无负环* 但题目要求"从1出发不存在负环"时输出"NO"* 这里条件为:!spfa() || h[1] == -1* 即:无负环 或 顶点1无出边*/if (!spfa() || h[1] == -1)  // 注意:spfa()返回true表示有负环{cout << "NO" << endl;  // 题目要求是从1出发不存在负环}else{cout << "YES" << endl;}}return 0;
}

【运行结果】

2
3 4
1 2 2
1 3 4
2 3 1
3 1 -3
NO
3 3
1 2 3
2 3 4
3 1 -8
YES
http://www.jsqmd.com/news/394621/

相关文章:

  • 2026年大件物流哪家强?口碑厂家精选指南,大件运输/大件物流,大件物流服务商口碑排行 - 品牌推荐师
  • 看完马斯克的“编程末日”预言,我反而松了一口气
  • 题解:洛谷 P4779 【模板】单源最短路径(标准版)
  • 矩阵乘法与同维空间互相转换(以二维为例)
  • 世毫九理论体系·正式总目录
  • 题解:洛谷 P1600 [NOIP 2016 提高组] 天天爱跑步
  • 美团礼品卡回收实用指南解决闲置困扰 - 京顺回收
  • 题解:洛谷 P2052 [NOI2011] 道路修建
  • 题解:洛谷 P1351 [NOIP 2014 提高组] 联合权值
  • 题解:洛谷 P5836 [USACO19DEC] Milk Visits S
  • YOLO26涨点改进 | 全网独家创新、注意力改进篇 | SCI一区 2025 | YOLO26引入MSCA多尺度稀疏交叉聚合,GCBAM分组注意力,助力遥感目标检测、图像分类任务有效涨点
  • 题解:洛谷 P3128 [USACO15DEC] Max Flow P
  • 题解:洛谷 P3379 【模板】最近公共祖先(LCA)
  • 题解:洛谷 P1395 会议
  • Claude Sonnet 4.6发布,操控计算机能力大幅提升,100万token上下文
  • 京东 e 卡闲置别浪费!可可收更安心,这样选最省心 - 可可收
  • 题解:洛谷 P3372 【模板】线段树 1
  • 研磨废水回用厂家怎么挑?2026年攻略来了,实验室废水处理/研磨废水回用(处理)/零排放清洗,研磨废水回用源头厂家找哪家 - 品牌推荐师
  • 题解:洛谷 P1099 [NOIP 2007 提高组] 树网的核
  • 闲置支付宝立减金别浪费!合规回收方法来了,新手也能轻松上手 - 可可收
  • Python3教程梳理
  • 题解:洛谷 P5908 猫猫和企鹅
  • 题解:洛谷 P5677 [GZOI2017] 配对统计
  • 2026沸石转轮一体机企业TOP榜:哪些品牌值得关注?催化燃烧/旋风除尘器/除尘器,沸石转轮制造厂家排行榜单 - 品牌推荐师
  • 瑞祥商联卡闲置不用?这样的合规回收方式,新手也能轻松上手 - 可可收
  • 2026年值得推荐的AVIF转WebP在线工具盘点(支持批量转换)
  • 微信立减金回收技巧:47%闲置率下,5招盘活你的“隐形财富” - 可可收
  • 2026年溴化锂中央空调选购指南:值得关注的公司,溴化锂冷水机组/二手溴化锂中央空调,溴化锂中央空调制造企业有哪些 - 品牌推荐师
  • PAM4相关概念
  • 2026年行业内评价高的调节阀厂商如何选,电液动盲板阀/蝶式止回阀/微阻缓闭止回阀/伸缩蝶阀,调节阀生产厂家哪家权威 - 品牌推荐师