题解【洛谷P2850】[USACO06DEC]虫洞Wormholes

题面

SPFA 判负环裸题。

SPFA 判断负环主要有 \(2\) 种方法:

  1. 判断每个点的出队次数是否 大于等于 \(n\)
  2. 判断每个点最短路径上点的个数是否 大于等于 \(n\)

这两种方法都很好证明。我个人比较推荐使用第二种方法。

因此,我们只要在 SPFA 的过程中记录一下每个点上最短路径上点的个数。

因为我们一开始将所有的点放进了队列中,所以 \(dist\) 数组就要初始化成 \(0\)

#include <bits/stdc++.h>

using namespace std;

const int N = 503, M = 5203;

int n, m, f, w;
int tot, head[N], ver[M], edge[M], nxt[M];
int dist[N], cnt[N];
bool st[N];

inline void add(int u, int v, int w)
{
    ver[++tot] = v, edge[tot] = w, nxt[tot] = head[u], head[u] = tot;
}

inline bool SPFA()
{
    memset(dist, 0, sizeof dist);
    memset(cnt, 0, sizeof cnt);
    memset(st, false, sizeof st);
    queue <int> q;
    for (int i = 1; i <= n; i+=1)
    {
        q.push(i); //一开始先将所有的点加入队列
        st[i] = true;
    }
    while (!q.empty())
    {
        int u = q.front(); q.pop();
        st[u] = false;
        for (int i = head[u]; i; i = nxt[i])
        {
            int v = ver[i], w = edge[i];
            if (dist[v] > dist[u] + w)
            {
                dist[v] = dist[u] + w;
                cnt[v] = cnt[u] + 1; //记录每个点最短路经的条数
                if (cnt[v] >= n) return true; //存在负环
                if (!st[v])
                {
                    st[v] = true;
                    q.push(v);
                }
            }
        }
    }
    return false;
}

int main()
{
    cin >> f;
    while (f--)
    {
        cin >> n >> m >> w;
        memset(head, 0, sizeof head);
        tot = 0;
        for (int i = 1; i <= m; i+=1)
        {
            int u, v, w;
            cin >> u >> v >> w;
            add(u, v, w), add(v, u, w);
        }
        for (int i = 1; i <= w; i+=1)
        {
            int u, v, w;
            cin >> u >> v >> w;
            add(u, v, -w);
        }
        if (SPFA()) puts("YES");
        else puts("NO");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xsl19/p/12399001.html