洛谷题单指南-最短路-P3385 【模板】负环
原题链接:https://www.luogu.com.cn/problem/P3385
题意解读:找到从1出发的负环,存在输出YES,不存在输出NO。
解题思路:
1、前置知识:SPFA算法 https://www.cnblogs.com/jcwy/p/18803811
2、核心思想:在SPFA算法中,每一次进行松弛操作,可以同步更新每个点到起点最短路的边数。
设cnt[i]表示i到起点的最短路边数,在松弛操作时对其同步更新:
if(dist[u] + w < dist[v])
{
dist[v] = dist[u] + w;
cnt[v] = cnt[u] + 1;
}
如果出现某个cnt[v]值>=n,则说明最短路有n条边,有n+1个点,由于一共只有n个点,必然存在环,且为负环。
3、扩展思考1:如果从起点无法走到负环,如何解决?
对于此情况,图形并不连通,可以假设一个虚拟起点s,s到所有点之间连一条长度为0的虚拟边,
这样,初始将s入队,并将其所有邻接点进行松弛操作后入队!
因此,我们可以初始直接将所有节点入队,并设置所有点的最短路径为0,再继续进行SPFA的处理即可。
4、扩展思考2:如果由于部分数据导致O(nm)复杂度超时,如何解决?
可以统计所有节点被松弛更新的总次数,那么如果超过一定阈值,可以想象可能意味着在负环中绕圈,负值设定是个经验值,比如2n、3n,在极端超时的情况下,往往可以解决问题。
还有一种方法,就是将队列换成堆栈,利用后进先出的特性会便于更快找到负环。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 6005;
int h[N], e[N], w[N], ne[N], idx;
int dist[N], cnt[N];
bool vis[N];
int n, m;
void add(int a, int b, int c)
{
e[++idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx;
}
bool spfa()
{
memset(dist, 0x3f, sizeof(dist));
memset(vis, 0, sizeof(vis));
memset(cnt, 0, sizeof(cnt));
dist[1] = 0;
queue<int> q;
q.push(1);
while(q.size())
{
int u = q.front(); q.pop();
vis[u] = false;
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(dist[u] + w[i] < dist[v])
{
dist[v] = dist[u] + w[i];
cnt[v] = cnt[u] + 1;
if(cnt[v]>= n) return true;
if(!vis[v])
{
q.push(v);
vis[v] = true;
}
}
}
}
return false;
}
int main()
{
int T;
cin >> T;
while(T--)
{
memset(h, -1, sizeof(h));
idx = 0;
cin >> n >> m;
while(m--)
{
int u, v, w;
cin >> u >> v >> w;
add(u, v, w);
if(w >= 0) add(v, u, w);
}
if(spfa()) cout << "YES" << endl;
else cout << "NO" << endl;
}
}
浙公网安备 33010602011771号