- 最近发现自己不太会判负环。
- 以下是两种科学的判负环方法,方法一的实际效率应当是比方法二快的。
- 方法一:
在 SPFA\text{SPFA}SPFA 的过程中顺便记一个变量 cnt[x]cnt[x]cnt[x] ,表示 1→x1\to x1→x 的最短路上经过的边数,在松弛时顺带判断,若存在一个点 xxx 满足 cnt[x]≥ncnt[x]\ge ncnt[x]≥n,则图中存在负环。 - 方法二:
在 SPFA\text{SPFA}SPFA 的过程中顺便记下每个点的入队次数,在松弛时顺带判断,若某时刻存在一个点的入队次数达到 nnn,则图中存在负环。 - 两种方法的时间复杂度均为 O(nm)\mathcal O(nm)O(nm),其他诸如 dfsSPFA\text{dfsSPFA}dfsSPFA 的奇怪的玄学算法的时间复杂度可能是指数级的,非常不科学,可以被负责任的出题人卡掉。
- 附方法一代码:
const int MaxNV = 2e3 + 5;
const int MaxNE = 6e3 + 5;
const int INF = 0x3f3f3f3f;
#define trav(u) for (int e = adj[u], v; v = to[e], e; e = nxt[e])
int n, m;
int dis[MaxNV], cnt[MaxNV];
int ect, adj[MaxNV], to[MaxNE], nxt[MaxNE], len[MaxNE];
inline void addEdge(int u, int v, int w)
{
nxt[++ect] = adj[u];
adj[u] = ect;
to[ect] = v;
len[ect] = w;
}
inline bool SPFA()
{
static bool inq[MaxNV];
static int que[MaxNV * MaxNE], qr;
que[qr = 1] = 1;
for (int i = 1; i <= n; ++i)
inq[i] = false;
for (int ql = 1, u; ql <= qr; ++ql)
{
inq[u = que[ql]] = false;
trav(u) if (tense(dis[v], dis[u] + len[e]))
{
cnt[v] = cnt[u] + 1;
if (cnt[v] >= n) return true;
if (!inq[v])
inq[que[++qr] = v] = true;
}
}
return false;
}