[模板]判负环

  • 负环大概指边权和为负的环
  • 给一个n个点m条边的有向图(如果是无向边加两次有向边应该就好啦),判断是否存在负环
  • n,m≤2e5

我一开始自己写了个最普通的bfs版的spfa然后光荣的TLE了三个点(一共才五个点)x

之后去百度了一下才知道还有dfs版的orz

个人理解:

平常写的bfs版的应该是可以防止爆栈之类的情况所以用队列做,复杂度O(kn),而这里的k是点进队的平均次数,而直接这样判负环就要判是否有某个点进队次数大于n次,这样子如果存负环最坏情况就很糟糕了(而且是经常这样),然后就有了下面的东西。

dfs版的spfa的判断是在松弛操作时判断这个结点是否第二次出现,如果是的话显然这条路径上就有负环了(如果没有的话自然不会走第二次),写的时候只要多开一个bool数组顺便记录一下某些点是否被访问过就好。

不过直接这样写据说还是会T掉(虽然我还没试_(:з」∠)_),然后下面的东西就是在wyhhhhhhhh大神的那篇博客里学来的优化技巧(趴)

就是说既然只需要判断是否有负环,那就直接找边权和为负的回路,一开始把dis数组(到点的距离)全部设为0然后跑,这样一开始只会扩展到负权的边。

之后我们再以每个节点开始跑一边spfa,如果路径上某个点第二次松弛那就直接跳出来啦。

注意记得回溯

边集数组记得开两倍

搜的时候xjb剪剪枝

最后还是贴一下代码(以后还是不折叠好了)x

#include<cstdio>
#include<cstring>

typedef long long lint;
const int N=200005;

struct edge{lint to,weight,next;}edges[N<<1];
lint n,m,t,cnt;
lint dis[N],head[N];
bool flag,vis[N];

inline lint read()
{
    lint s=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){s=s*10+c-'0';c=getchar();}
    return s*f;
}
inline void addEdge(lint u,lint v,lint w)
{
    edges[++cnt]=(edge){v,w,head[u]};
    head[u]=cnt;
}
inline void spfa(int cur)
{
    if(flag)return;
    vis[cur]=1;
    for(int i=head[cur];i;i=edges[i].next)
    {
        if(flag)return;
        if(dis[cur]+edges[i].weight<dis[edges[i].to])
        {
            dis[edges[i].to]=dis[cur]+edges[i].weight;
            if(vis[edges[i].to])
            {
                flag=1;return;
            }else spfa(edges[i].to);
        }
    }vis[cur]=0;
}
int main()
{
    //freopen("input.in","r",stdin);
    t=read();
    while(t--)
    {
        memset(head,0,sizeof(head));
        memset(vis,0,sizeof(vis));
        memset(dis,0,sizeof(dis));
        cnt=flag=0;
        
        n=read();m=read();
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            u=read();v=read();w=read();
            addEdge(u,v,w);
            if(w>=0)addEdge(v,u,w);
        }
        for(int i=1;i<=n;i++)
        {
            spfa(i);
            if(flag)break;
        }
        printf(flag?"YE5":"N0");
        printf("\n");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/yoshinow2001/p/7450139.html