zoukankan      html  css  js  c++  java
  • [模板]判负环

    • 负环大概指边权和为负的环
    • 给一个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;
    }
  • 相关阅读:
    CodeForces:847D-Dog Show
    CodeForces 699C
    CodeForces:699B-One Bomb
    哈夫曼树:HDU5884-Sort(队列、哈夫曼树)
    Educational Codeforces Round 31- D. Boxes And Balls
    经典:区间dp-合并石子
    Codeforces Round #879 (Div. 2) C. Short Program
    卡顿
    异常断点
    自动布局
  • 原文地址:https://www.cnblogs.com/yoshinow2001/p/7450139.html
Copyright © 2011-2022 走看看