负环详解(暂无题目推荐)

只谈情不闲聊 提交于 2019-11-27 11:58:50

什么是负环?

顾名思义,就是一个所有边的边权和为负数的环

出现负环会怎么样?

我们知道,一般情况下,图上的最短路都是确定的。但是一旦图上有一个负环,\(s\)\(t\)的最短路就会不远千里的去覆盖上这个环(只要能够到达),并且不厌其烦的走上一遍又一遍。由于负环的边权和是负的,并且它是一个环,也就是说走一遍和走无数遍都停留在进入的那个点。那么最短路每经过一次这个负环,这个费用都会缩小一点,如果经过了无数次,也就是无穷小,也就是不存在最短路。当然这里有一个限定,就是每个点经过的次数不能超过\(1\)次。

既然负环的影响这么大,那么就要引出我们的下一个问题了。

怎么判定负环?

相信大家都学过至少两种最短路的计算方法,而且一定也都会\(dijkstra\)\(SPFA\)这两种算法(如果不会的请先去学习SPFA再来观看此博客)。花开两朵,各表一枝。我们这里只谈\(SPFA\)。大家都知道,\(SPFA\)的判定方式就是不断地收紧每个点到起点的最短路径,每次都不一定会收到最紧,但只要有解,最终一定会收成最紧(这也正是它这么好卡的原因,一点一点的,能不慢吗)。我们前面提到过,如果存在负环,那么最短路会不断缩小至无穷小。那么这里我们就可以应用它的这一特点。在\(SPFA\)中,每个点最短被其他\(n-1\)个点各收紧一次,如果被收紧了\(n\)次,显然是不合理的。那么我们就记录每个点被收紧的次数,有任何点超过\(n\)次,就可以判定存在负环了,如果\(SPFA\)成功运行完了,就证明不存在负环。

奇妙的是,我使用单调队列优化\(SPFA\)没有过这道题,所以大家还是用普通的\(SPFA\)来判定吧(毕竟出题人总不可能让你没法判定)。

题目链接

下面是代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
#include<queue>
#define ll long long
#define gc getchar
#define maxn 2050
#define maxm 3050
using namespace std;

inline ll read(){
    ll a=0;;int f=0;char p=gc();
    while(!isdigit(p)){f|=p=='-';p=gc();}
    while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
    return f?-a:a;
}int t,n,m;

struct ahaha{
    int w,to,next;
}e[maxm<<1];int tot,head[maxn];
inline void add(int u,int v,int w){
    e[tot]={w,v,head[u]};head[u]=tot++;
}

queue<int>q;
int d[maxn],s[maxn];
inline bool spfa(){d[1]=0;
    q.push(1);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].to;if(d[v]<=d[u]+e[i].w)continue;
            if(s[v]+1==n)return 1;++s[v];d[v]=d[u]+e[i].w;
            q.push(v);
        }
    }return 0;
}

inline void clear(){tot=0;
    memset(head,-1,sizeof head);
    memset(d,63,sizeof d);
    memset(s,0,sizeof s);
}

int main(){
    t=read();
    while(t--){clear();
        n=read();m=read();
        for(int i=1;i<=m;++i){
            int u=read(),v=read(),w=read();
            add(u,v,w);if(w>=0)add(v,u,w);
        }
        puts(spfa()?"YE5":"N0");
        while(!q.empty())q.pop();
    }
    return 0;
}

很久没有接触过OI了,有些生疏,也就不放例题了(其实是没时间找了)。

不知我的博客是否对你有帮助呢?如果对你有些许帮助,不妨点个推荐吧。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!