『笔记』SPFA判断负权环

基本定义

什么是负权环??

负权环指的是图上的某些边首尾相连构成的,边权和相加小于零的一条环。

例如:

下图中 \(1 \to 2\) \(\to 3\) \(\to 1\) 就是一条负权环,权值和为 \(-1\) 。而 \(2 \to 3\) \(\to 4\) \(\to 5\) 则是一条环而不是负权环,其权值和为 \(10\)

性质

显而易见,如果某图存在负权环,那么求最短路的时候如果不特殊处理,那么就会导致程序在负权环上一直转圈圈,所谓的最短路权值和就会越转越小,知道程序优美 \(TLE\) 掉。

基本操作

如何判断负权环呢??

死去的SPFA就是一大利器!!

“请求最短路”:关于____,它____。

“本题存在负环”:关于____,它复活了?!

SPFA BFS型

优缺点

  • 代码简单,思路简洁,和求最短路的 \(SPFA\) 代码相似度高。

  • 比较慢,容易超时。

实现思路

  • 对每一个进行最短路求权,对每一条边进行最短路松弛。

  • 如果有某一个点入队次数超过节点总数,则说明存在负环。

  • 正确性:

    对于 \(BFS\) ,一次 \(BFS\) 中,没有负环的情况下一个点最多入队 \(n\) 次(从源点出发连向所有其它点,而所有其他点又指向一个点,每个点正好将这个点更新一次,入队一次)。

    如果说还能再更新一次这个点,那一定是这个点出发跑了一个负环再到达自己,\(num\) 在进入是否入队的判断中执行。

代码

bool SPFA_BFS(int st)
{
    q.push(st);
    vis[st] = true;
    while (!q.empty())
    {
        int u = q.front();

        q.pop();
        vis[u] = false;

        for (int i = head[u]; i; i = e[i].nxt)
        {
            int v = e[i].to;

            if (dis[v] > dis[u] + e[i].dis)
            {
                dis[v] = dis[u] + e[i].dis; //最短路松弛

                num[v] = num[u] + 1; //记录某节点出现次数

                if (num[v] >= n) //若大于总节点数,则存在负环
                    return true;

                if (!vis[v])
                {
                    num[v]++;
                    /*
                    if(num[v]>=n)
                        return true;
                    */
                    q.push(v);
                    vis[v] = true;
                }
            }
        }
    }
    return false;
}

SPFA DFS型

优缺点

  • 效率相对 \(BFS\) 直接起飞。 虽然特殊构造的图还是会爆炸。。。

  • 一次质能处理判断一个源点出发是否存在负权环。如果要判断整个图,就需要拓展 \(n\) 个节点。

实现思路

  • 在一次搜索中,一旦一个点更新了两次,就说明有负环

  • 正确性:
    首先搜到一个点两次肯定是有环的,然后第一次更新完了第二次又更新了,除非环的权值是负的,否则不可能更新,于是这样就简单的找到了负环。

代码

bool check(int st)
{
    vis[st] = true;

    for (int i = head[st]; i; i = e[i].nxt)
    {
        int v = e[i].to;

        if (dis[v] > dis[st] + e[i].dis)
        {
            dis[v] = e[i].dis + dis[st];

            if (!vis[v])
            {
                if (SPFA_DFS(v)) //前面搜到了负环
                    return true;
            }
            else
                return true; //一次深搜中搜了一个点两次,则存在负环。
        }
    }
    vis[st] = false;

    return false;
}

典型例题

P2850 [USACO06DEC]Wormholes G

#10085. 「一本通 3.3 练习 2」虫洞 Wormholes

温馨提示:珍爱生命,多组数据记得初始化。

posted @ 2021-02-01 16:27  Frather  阅读(213)  评论(4编辑  收藏  举报