zoukankan      html  css  js  c++  java
  • 『笔记』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

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

  • 相关阅读:
    表达式树作为条件封装多表连查
    EF之结构进一步优化
    EF之ExecuteSqlCommand更新出现无效的解决方案
    dynamic与匿名对象
    webapi 通过dynamic 接收可变参数
    EF INNER JOIN,LEFT JOIN,GROUP JOIN
    Linq join on 多条件
    Excel 行列转置 解决竖向拉,字母跟着递增的问题
    Windows7 安装vs2015 之后 调试Web项目IIS启动不了 aspnetcore.dll未能加载
    Mysql 服务在本机,需要单机调试Mysql数据库 发生 不认识hostname‘localhost’
  • 原文地址:https://www.cnblogs.com/Frather/p/14356999.html
Copyright © 2011-2022 走看看