每周一算法:负环判断

2024-04-19 16:52
文章标签 算法 判断 每周 负环

本文主要是介绍每周一算法:负环判断,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目链接

负环

题目描述

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

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

输入格式

本题单测试点有多组测试数据

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

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

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

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

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

NO
YES

提示

数据规模与约定

对于全部的测试点,保证:

  • 1 ≤ n ≤ 2 × 1 0 3 1 \leq n \leq 2 \times 10^3 1n2×103 1 ≤ m ≤ 3 × 1 0 3 1 \leq m \leq 3 \times 10^3 1m3×103
  • 1 ≤ u , v ≤ n 1 \leq u, v \leq n 1u,vn − 1 0 4 ≤ w ≤ 1 0 4 -10^4 \leq w \leq 10^4 104w104
  • 1 ≤ T ≤ 10 1 \leq T \leq 10 1T10
提示

请注意, m m m 不是图的边数。

算法思想

判断图中是否存在负环,需要先了解下面关于最短路的几个性质:

  • 对于边权为正的图,任意两个节点之间的最短路,不会经过重复的节点。
  • 对于边权为正的图,任意两个节点之间的最短路,不会经过重复的边。
  • 对于边权为正的图,任意两个节点之间的最短路,任意一条的节点数不会超过 n n n,边数不会超过 n − 1 n-1 n1

Bellman–Ford 算法

Bellman–Ford 算法是一种基于松弛(relax)操作的最短路算法,可以求出有负权的图的最短路,并可以对最短路不存在的情况进行判断。大名鼎鼎的「SPFA」,就是 Bellman–Ford算法的一种实现。

基本思想

Bellman–Ford算法所做的,就是不断尝试对图上每一条边进行松弛。每进行一轮循环,就对图上所有的边都尝试进行一次松弛操作,当一次循环中没有成功的松弛操作时,算法停止。

对于边 ( u , v ) (u,v) (u,v),Bellman–Ford算法中松弛操作对应下面的式子: d i s ( v ) = min ⁡ ( d i s ( v ) , d i s ( u ) + w ( u , v ) ) dis(v) = \min(dis(v), dis(u) + w(u, v)) dis(v)=min(dis(v),dis(u)+w(u,v))。尝试用 S → u → v S \to u \to v Suv(其中 S → u S \to u Su 的路径取最短路)这条路径去更新 v v v 点最短路的长度,如果这条路径更优,就进行更新。

每次循环的时间复杂度是 O ( m ) O(m) O(m),那么最多会循环多少次呢?

在最短路存在的情况下,由于一次松弛操作会使最短路的边数至少 + 1 +1 +1,而最短路的边数最多为 n − 1 n-1 n1,因此整个算法最多执行 n − 1 n-1 n1 轮松弛操作。故总时间复杂度为 O ( n m ) O(nm) O(nm)

但还有一种情况,如果从 S S S 点出发,抵达一个负环时,松弛操作会无休止地进行下去。对于最短路存在的图,松弛操作最多只会执行 n − 1 n-1 n1 轮,因此如果第 n n n 轮循环时仍然存在能松弛的边,说明从 S S S 点出发,能够抵达一个负环。

代码实现

struct Edge {int u, v, w;
};vector<Edge> edge;int dis[MAXN], u, v, w;
const int INF = 0x3f3f3f3f;
//节点数n,起点s
bool bellmanford(int n, int s) {memset(dis, 0x3f, sizeof(dis));dis[s] = 0;bool flag = false;  // 判断一轮循环过程中是否发生松弛操作for (int i = 1; i <= n; i++) {flag = false;for (int j = 0; j < edge.size(); j++) {u = edge[j].u, v = edge[j].v, w = edge[j].w;if (dis[u] == INF) continue;// 无穷大与常数加减仍然为无穷大// 因此最短路长度为 INF 的点引出的边不可能发生松弛操作if (dis[v] > dis[u] + w) {dis[v] = dis[u] + w;flag = true;}}// 没有可以松弛的边时就停止算法if (!flag) {break;}}// 第 n 轮循环仍然可以松弛时说明 s 点可以抵达一个负环return flag;
}

队列优化的Bellman–Ford

SPFA即 Shortest Path Faster Algorithm,即队列优化的Bellman–Ford。很多时候Bellman–Ford算法并不需要那么多无用的松弛操作, 只有上一次被松弛的结点,所连接的边,才有可能引起下一次的松弛操作。那么可以用队列来维护哪些结点可能会引起松弛操作,就能只访问必要的边了。

SPFA也可以用于判断 s s s点是否能抵达一个负环,只需记录最短路经过了多少条边,当经过了至少 n n n条边时,说明 s s s点可以抵达一个负环。

代码实现

struct edge {int v, w;
};vector<edge> e[maxn];
int dis[maxn], cnt[maxn], vis[maxn];
queue<int> q;bool spfa(int n, int s) {memset(dis, 0x3f, sizeof(dis));dis[s] = 0, vis[s] = 1;q.push(s);while (!q.empty()) {int u = q.front();q.pop(), vis[u] = 0;for (auto ed : e[u]) {int v = ed.v, w = ed.w;if (dis[v] > dis[u] + w) {dis[v] = dis[u] + w;cnt[v] = cnt[u] + 1;  // 记录最短路经过的边数if (cnt[v] >= n) return false;// 在不经过负环的情况下,最短路至多经过 n - 1 条边// 因此如果经过了多于 n 条边,一定说明经过了负环if (!vis[v]) q.push(v), vis[v] = 1;}}}return true;
}

虽然在大多数情况下SPFA跑得很快,但其最坏情况下的时间复杂度为 O ( n m ) O(nm) O(nm),将其卡到这个复杂度也是不难的,所以考试时要谨慎使用。在没有负权边时最好使用Dijkstra算法,在有负权边且题目中的图没有特殊性质时,若SPFA是标算的一部分,题目不应当给出Bellman–Ford算法无法通过的数据范围。

这篇关于每周一算法:负环判断的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/918110

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)

poj 3259: 题意:John的农场里n块地,m条路连接两块地,w个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。 任务是求你会不会在从某块地出发后又回来,看到了离开之前的自己。 判断树中是否存在负权回路就ok了。 bellman代码: #include<stdio.h>const int MaxN = 501;//农场数const int

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

dp算法练习题【8】

不同二叉搜索树 96. 不同的二叉搜索树 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n = 3输出:5 示例 2: 输入:n = 1输出:1 class Solution {public int numTrees(int n) {int[] dp = new int

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯: