先放一张图加深理解负环
所谓负环,即权值之和为负值的环
此图中执行SPFA会进入负环的死循环(每次加边后都比原来值小)
故应cnt[ ]记录下每个点遍历次数
若大于总点数,则存在负环
例题 :洛谷—负环模板
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define M 100010
#define INF 0x3f3f3f3f
struct edge
{
int to,w;//保存边的信息,包括边的终点以及权值
};
int cnt[M];//用于记录该点入队次数
int flag = 0;
long long dis[M]; //最短距离的估计值(当前该点的最短距离)
bool inq[M]; //标记该点是否在队列之中
vector<edge> g[M]; //利用一个vector保存,g[i]表示以i为起点的所有边的信息
int n,m,ee;
void spfa(int u)
{
for(int i = 0;i <= n;i++) //初始化
{
dis[i] = INF; //将估计值都初始化为INF
inq[i] = false;
cnt[i] = 0; //初始化为不在队列中
}
dis[u] = 0; //起点的估计值直接就是0
inq[u] = true; //加入队列并进行标记
queue<int> q;
q.push(u);
while(!q.empty())
{
u = q.front();
inq[u] = false;
q.pop();
for(int i = 0;i < g[u].size();i++)
{
int v = g[u][i].to; //找出这条边对应的终点
int w = g[u][i].w; //这条边对应的权值
if(cnt[v] > n){//此处判负环,由于最短路长度不能大于总点数,则进入负环的死循环
flag = 1;
return ;
}
if(dis[v] > dis[u]+w) //如果终点的最短距离比起点的最短距离加上这条边的权值大 那么就更新
{
dis[v] = dis[u]+w;
cnt[v] = cnt[u] + 1;//一处优化,计算是否进入负环死循环中
if(cnt[v] > n){
flag = 1;
return ;
}
if(!inq[v]) //如果v点的最短距离有所更新并且不在队列中,就将其加入队列。
{ //否则就不需要重复加入队列增加不必要的操作。
inq[v] = true; //加入队列并标记
q.push(v);
}
}
}
}
}
int main()
{
int T;
cin >> T;
while(T--)
{
flag = 0;
scanf("%d%d",&n,&m);
for(int i = 0;i <= 2005;i++) //清空vector避免多kase相互影响
g[i].clear();
for(int i = 0;i < m;i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
edge e;
if(c >= 0){
e.to = b;e.w = c;
g[a].push_back(e);
e.to = a;
g[b].push_back(e);
}else{
e.to = b;e.w = c;
g[a].push_back(e);
}
}
spfa(1);
if(flag){
printf("YE5\n");
}else{
printf("N0\n");
}
}
return 0;
}