题解:P3385 【模板】负环
思路
负环就是有向图上一个边权之和是负数的环。
我们通常使用 Bellman-Ford 及其衍生算法 SPFA 解决此问题。
由于 SPFA 并没有在最差情况下比 Bellman-Ford 快,并且每一个大赛出题人都应该知道如何卡掉常见的 SPFA,所以并不建议使用 SPFA 替代 Bellman-Ford,如果 Bellman-Ford 都跑不过去就应该更换思路。你用来骗分当我没说。
想要了解 Bellman-Ford 就要先了解松弛操作。
对于边权为
这么做的含义就是尝试用
而 Bellman-Ford 就是不断对图上每一条边进行松弛。每一次循环,都对所有边进行一次松弛操作。
所以当最短路存在时,需要循环多少次呢?
当最短路存在时,每一次松弛操作都会让最短路长度加一,而最短路长度最长为
注意,是在最短路存在时最多需要
代码
#include <bits/stdc++.h>
using namespace std;
const int N=2e3+10;
vector<pair<int,int>> mp[N];//图
int dis[N];//最短路长度。
int main()
{
int T;
cin>>T;
while (T--)
{
int n,m;
cin>>n>>m;
for (int i=1; i<=n; i++)
{
mp[i].clear();
}
for (int i=1; i<=m; i++)//输入图。
{
int u,v,w;
cin>>u>>v>>w;
mp[u].push_back({v,w});
if(w>=0)
{
mp[v].push_back({u,w});
}
}
memset(dis,0x3f,sizeof(dis));//初始正无穷。
dis[1]=0;//源点距离是0。
for (int i=1; i<n; i++)
{
bool flag=false;
for (int j=1; j<=n; j++)
{
for (auto it:mp[j])
{
if (dis[j]!=0x3f3f3f3f&&dis[it.first]>dis[j]+it.second)//进行松弛。
{
dis[it.first]=dis[j]+it.second;
flag=true;
}
}
}
if (!flag)//不能再松弛。
{
break;
}
}
bool flag=false;
for (int i=1; i<=n; i++)
{
for (auto it:mp[i])
{
if (dis[i]!=0x3f3f3f3f&&dis[it.first]>dis[i]+it.second)
{
flag=true;//有负环。
break;
}
}
if (flag)
{
break;
}
}
if (flag)
{
cout<<"YES\n";
}
else
{
cout<<"NO\n";
}
}
return 0;
}