P3385 【模板】负环 题解
差分约束系统是什么?
差分约束系统指的是一个序列 \(x={x_1,x_2,\cdots x_m}\) 和以如下形式出现的 \(n\) 元一次不等式组。
\[\left\{\begin{matrix}
x_{i_1}-x_{j_1}\le c_{k_1}
\\x_{i_2}-x_{j_2}\le c_{k_2}
\\\vdots
\\x_{i_n}-x_{j_n}\le c_{k_n}
\end{matrix}\right.
\]
还是说说前置知识。
P3385 【模板】负环
Description
给你一个 \(n\) 个点的有向图,让你判断图中是否有能从 \(1\) 号点出发可以到达的负环。
负环定义为一条边权和为负的回路。
Solution
思考负环的一些性质。
容易发现负环上一点与任意一点的最短路都不存在,因为你可以跑无数遍负环,边权和会越来越大。我们需要做的是找出一个限度值,如果超过了这个限度值就知道出现了负环。
注意到跑完一次单源最短路最多也就是把所有节点全部入队更新。如果点数为 \(n\),同一个节点最多只可能入队 \(n-1\) 次。
所以,开一个数组 cnt 记录每个节点入队的次数,如果次数超过 \(n-1\) 就说明有负环。
SPFA 在卡满情况下与朴素 Bellman-Ford 复杂度都为 \(O(nm)\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
long long T,n,m,tot,head[2005],dis[2005],cnt[2005];
bool vis[2005];
queue<int>q;
struct node{
int from,to,w,nxt;
}e[6005];
inline void add_edge(int u,int v,int w){
e[++tot].from=u;
e[tot].to=v;
e[tot].w=w;
e[tot].nxt=head[u];
head[u]=tot;
return;
}
inline bool SPFA(int s){
memset(dis,63,sizeof(dis));
dis[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to,w=e[i].w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(!vis[v]){
cnt[v]++;
if(cnt[v]>=n){
return 1;
}
vis[v]=1;
q.push(v);
}
}
}
}
return 0;
}
signed main(){
cin>>T;
while(T--){
tot=0;
memset(head,0,sizeof(head));
memset(e,0,sizeof(e));
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
while(!q.empty()){
q.pop();
}
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
if(w>=0){
add_edge(u,v,w);
add_edge(v,u,w);
}
else{
add_edge(u,v,w);
}
}
if(SPFA(1)){
cout<<"YES"<<endl;
}
else{
cout<<"NO"<<endl;
}
}
return 0;
}

浙公网安备 33010602011771号