spfa判负环

本文介绍使用SPFA算法判断图中是否存在负环的方法,包括BFS和DFS两种形式,并提供详细的C++代码实现。

输入输出格式

输入格式:

 

第一行一个正整数T表示数据组数,对于每组数据:

第一行两个正整数N M,表示图有N个顶点,M条边

接下来M行,每行三个整数a b w,表示a->b有一条权值为w的边(若w<0则为单向,否则双向)

 

输出格式:

 

共T行。对于每组数据,存在负环则输出一行"YE5"(不含引号),否则输出一行"N0"(不含引号)。

 

输入输出样例

输入样例#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: 
N0
YE5

说明

N,M,|w|≤200 000;1≤a,b≤N;T≤10 

大提醒:多组数据初始化一定要彻底

1.spfa的BFS形式判负环,但是比较慢,经常会T

思想:如果有一个点入队超过n次,说明存在负环。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<queue>
 4 #define inf 2147483600
 5 using namespace std;
 6 const int N=500002;
 7 struct node{
 8     int v,c,ne;
 9 }e[N*2];
10 int n,m,tot,h[N];
11 void add(int u,int v,int c)
12 {
13     tot++;e[tot].v=v;e[tot].c=c;e[tot].ne=h[u];h[u]=tot;
14 }
15 int v[N],d[N],in[N],cnt[N];
16 queue<int>q;
17 bool fg;
18 void spfa(int s)
19 {
20     for(int i=1;i<=n;i++) d[i]=inf,v[i]=0;
21     v[s]=1;d[s]=0;q.push(s);
22     while(!q.empty())
23     {
24         int ff=q.front();q.pop();v[ff]=0;
25         in[ff]=1;cnt[ff]++;
26         if(cnt[ff]>=n+1){fg=1;return;}
27         for(int i=h[ff];i;i=e[i].ne)
28         {
29             int rr=e[i].v;
30             if(d[rr]>d[ff]+e[i].c)
31             {
32                 d[rr]=d[ff]+e[i].c;
33                 if(!v[rr])
34                  v[rr]=1,q.push(rr);
35             }
36         }
37     }
38 }
39 int T;
40 int main()
41 {
42     scanf("%d",&T);
43     while(T--)
44     {
45          scanf("%d%d",&n,&m);
46          for(int i=1;i<=n;++i) h[i]=in[i]=cnt[i]=0;
47          tot=0;fg=0;
48          for(int i=1;i<=m;i++)
49          {
50              int x,y,z;scanf("%d%d%d",&x,&y,&z);
51              add(x,y,z);
52               if(z>0) add(y,x,z);    
53          }
54          for(int i=1;i<=n;++i)
55          {
56              if(!in[i]) spfa(i);
57              if(fg) break;
58          }
59          if(!fg)cout<<"N0"<<endl;
60          else cout<<"YE5"<<endl;
61     }
62     return 0;
63 }
View Code

2.spfa的DFS形式判负环,比BFS要快很多

思想:初始化,d[i]=0;枚举每个点去找负环

再进行松弛操作的同时如果一个点被松弛到两次,说明存在负环。

因为初始dis为0,所以只能通过负边来更新d[],而一但一个点被松弛了多次,那么一定存在负环。

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<queue>
 4 #define inf 2147483600
 5 using namespace std;
 6 const int N=500002;
 7 struct node{
 8     int v,c,ne;
 9 }e[N*2];
10 int n,m,tot,h[N];
11 void add(int u,int v,int c)
12 {
13     tot++;e[tot].v=v;e[tot].c=c;e[tot].ne=h[u];h[u]=tot;
14 }
15 int v[N],d[N],in[N],cnt[N];
16 queue<int>q;
17 bool fg;
18 void spfa(int u)
19 {
20    if(fg) return;
21    v[u]=1;
22    for(int i=h[u];i;i=e[i].ne)
23    {
24           int rr=e[i].v;
25           if(d[rr]>d[u]+e[i].c)
26           {
27                 d[rr]=d[u]+e[i].c;
28                 if(v[rr] || fg){fg=1;break;}
29                 spfa(rr);
30           }
31    }
32    v[u]=0;
33 }
34 int T;
35 int main()
36 {
37     scanf("%d",&T);
38     while(T--)
39     {
40          scanf("%d%d",&n,&m);
41          for(int i=1;i<=n;++i) h[i]=v[i]=0,d[i]=0;
42          tot=0;fg=0;
43          for(int i=1;i<=m;i++)
44          {
45              int x,y,z;scanf("%d%d%d",&x,&y,&z);
46              add(x,y,z);
47               if(z>0) add(y,x,z);    
48          }
49          for(int i=1;i<=n;++i)
50          {
51              spfa(i);
52              if(fg) break;
53          }
54          if(!fg)cout<<"N0"<<endl;
55          else cout<<"YE5"<<endl;
56     }
57     return 0;
58 }
View Code

 

转载于:https://www.cnblogs.com/adelalove/p/8492193.html

### SPFA算法检测的实现及原理 SPFA(Shortest Path Faster Algorithm)是一种用于解决单源最短路径问题的有效算法,尤其适用于含有权边但不包含的情况。然而,在实际应用中,如果图中存在SPFA可能陷入无限循,因此需要一种机制来检测这种特殊情况。 #### 的存在条件 指的是在一个图中存在一条回路,该回路上所有边的权重之。由于每次经过这条回路都会使路径长度变得更小,理论上可以不断绕圈从而得到无穷小的距离值[^1]。 #### SPFA算法的工作方式 SPFA的核心思想是利用队列进行广度优先搜索(BFS),并通过松弛操作更新节点到起点之间的距离。具体来说,对于每一个从队列弹出的顶点u,遍历它所有的邻接点v,并尝试用当前已知的最佳路径加上(u,v)这条边的成本去改进到达v点的距离。一旦发现新的更优解,则将v重新放入队列等待进一步探索[^3]。 #### 检测的关键技术 尽管标准版SPFA不具备内置功能自动识别,但可以通过增加额外计器变量`cnt[]`记录每个结点被访问(即入队)的最大次来间接完成此任务: - **初始化阶段**: 设置组 `dis[]` 表示各顶点至源点间的最小估计成本;设初值均为正无穷大(+∞),除了起始位置设置成零外(`dis[s]=0`)。另外定义辅助组 `inQueue[]`,用来标记哪些顶点正处于队列之中以防重复插入相同元素浪费时间资源。 - **核心逻辑修改**: - 当某顶点再次进入队列前检查它的累计入队目是否已经超过整个网络中的总定点量V; 如果满足上述条件则立即终止运算并报告发现了不可接受状况—存在至少一个权闭合链表结构[^2]. 以下是具体的伪代码描述如何扩展基础版本以支持探测: ```python from collections import deque def spfa_with_negative_cycle_detection(graph, start_node, num_nodes): dis = [float('inf')] * (num_nodes + 1) cnt = [0] * (num_nodes + 1) in_queue = [False] * (num_nodes + 1) queue = deque() dis[start_node] = 0 queue.append(start_node) in_queue[start_node] = True while queue: u = queue.popleft() in_queue[u] = False for v, weight in graph[u]: if dis[v] > dis[u] + weight: dis[v] = dis[u] + weight if not in_queue[v]: cnt[v] += 1 # If a node is relaxed more than the number of nodes times, # then there must be a negative cycle. if cnt[v] >= num_nodes: return "Negative Cycle Detected" queue.append(v) in_queue[v] = True return "No Negative Cycles Found" ``` 在这个增强型函里,我们引入了一个名为`cnt[]`的新列表跟踪各个节点经历过的放松迭代轮次。每当某个特定节点准备第二次加入工作序列之前都要先核查其对应统计值是不是已经达到了预设界限——也就是等于总的节点个N。如果是的话就意味着系统内部必然隐藏着某种式上的面反馈闭现象发生,此时应该立刻停止后续计算动作并向外界发出警告信号表明遇到了非法情[^4]。 ### 结论 综上所述,虽然原始态下的SPFA并不自带针对拓扑结构的有效甄别手段,不过借助简单的附加措施比如设立专门监控指标就可以轻松弥补这项缺陷进而构建更加健壮可靠的解决方案出来供人们日常开发实践当中选用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值