• 模板---负环(学习笔记)


    判断负环:DFS+SPFA BFS+SPFA

    https://www.luogu.org/problemnew/show/P3385

    原理:DFS判断负环:从某一个点开始DFS又回到了这个点.BFS判断负环:某一个点进出队列超过n次

    判断负环这个东西比较玄学,有的题目卡DFS,有的题目卡BFS.

    DFS+SPFA

    #include<bits/stdc++.h>
    using namespace std;
    int T,n,m,a,b,c,num_edge;
    bool flag,bj[20005];
    int head[20005],w[20005];
    struct Edge{
        int to,nxt,dis;
    }edge[100005];//结构体存边
    void add_edge(int x,int y,int z){
        edge[++num_edge].nxt=head[x];
        edge[num_edge].to=y;
        edge[num_edge].dis=z;
        head[x]=num_edge;
    }//链式前向星存图
    void spfa(int x){
        if(flag==true) return; //优化
        bj[x]=true;            //标记x点被DFS过
        for(int i=head[x];i;i=edge[i].nxt){//枚举与该点相邻的每一条边
            int y=edge[i].to;   //这条边连接的点
            int z=edge[i].dis;  //这条边的权值
            if(w[y]>w[x]+z){    //松弛操作
                w[y]=w[x]+z;
                if(bj[y]==true){ //如果这个点之前走过,说明存在环
                    flag=true;
                    return;
                } 
                spfa(y); //否则继续DFS下去
            }
        }
        bj[x]=false; //回溯
        return;
    }
    int main(){
        scanf("%d",&T);
        while(T--){ //T组数据
            flag=false;
            memset(w,0,sizeof(w)); 
            memset(bj,0,sizeof(bj));
            memset(head,0,sizeof(head));
            memset(edge,0,sizeof(edge));
            //多组数据一定要注意重置初始化
            scanf("%d%d",&n,&m);
            for(int i=1;i<=m;i++){
                scanf("%d%d%d",&a,&b,&c);
                add_edge(a,b,c);
                if(c>=0) add_edge(b,a,c);
           //根据题目要求,如果这条边边的权值大于0,建双向边
            }
            for(int i=1;i<=n;i++){
                spfa(i);
                if(flag==true) break;
           //找到负环,做个标记,直接退出循环
            }
            if(flag==true) printf("YE5
    ");
            else printf("N0
    ");
          //这道题的输入真的有毒,你发现了吗?
        }
        return 0;
    }
    
    

    BFS+SPFA

    #include<bits/stdc++.h>
    #define N 100005
    using namespace std;
    int n,m,T;
    bool vis[N];
    int head[N],tot,dis[N],cnt[N],q[N];
    struct node{int nxt,to,w;}edge[N<<1];
    //因为有双向边,所以数组长乘2
    void add(int x,int y,int z){
       edge[++tot].nxt=head[x];
       edge[tot].to=y;
       edge[tot].w=z;
       head[x]=tot;
    }
    inline bool spfa(int s){
        int l=0,r=0;  //队列的头尾指针
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        memset(cnt,0,sizeof(cnt));
        memset(q,0,sizeof(q)); //多组数据重置
        vis[s]=true;cnt[s]=1;dis[s]=0; q[r++]=s;
       //队列初始化,vis标记是否在队列里;
       //cnt记录该点进出队列的次数,dis距离,q模拟队列;
        while(l!=r){
            int u=q[l++]; //出队
            if(l>n) l=0; //重复使用指针l,r,可理解为循环队列,下文同理
            vis[u]=false;//标记为不在队列里
            for(int i=head[u];i;i=edge[i].nxt){
                if(dis[edge[i].to]>dis[u]+edge[i].w){ //松弛操作
                    dis[edge[i].to]=dis[u]+edge[i].w;
                    cnt[edge[i].to]=cnt[u]+1;//记录入队次数
                    if(cnt[edge[i].to]>=n&&edge[i].w<0)                     				
                        return true;
            //判断负环:一个点进出队列的次数超过n次,且权值为负
                    if(!vis[edge[i].to]){
                        vis[edge[i].to]=true;//不在队列就入队
                        if(dis[edge[i].to]>dis[q[l]]){
                            l--;
                            if(l<0) l=n;
                            q[l]=edge[i].to;
                        }
                        else{
                            q[r++]=edge[i].to;
                            if(r>n) r=0;
                        }
                    }
                }
            }
        }
        return false;
    }
    int main(){
        scanf("%d",&T);
        while(T--){
    	memset(head,0,sizeof(head));
            scanf("%d%d",&n,&m);
            for(int i=1,u,v,w;i<=m;i++){
                scanf("%d%d%d",&u,&v,&w);
                add(u,v,w);
                if(w>=0) add(v,u,w);
            }
            if(spfa(1)) printf("YE5
    ");
            else printf("N0
    ");
        }
    }
    
  • 相关阅读:
    Spider爬虫清洗数据(re方法)
    Python 操作 mongodb 数据库
    python操作mysql数据库
    BeautifulSoup高级应用 之 CSS selectors /CSS 选择器
    mongoDB在centos7上的安装
    CentOS7安装mongoDB数据库
    [洛谷P4602] CTSC2018 混合果汁
    [洛谷P2605] ZJOI2016 基站选址
    [CF1039D] You Are Given a Tree
    [CF1105E] Helping Hiaset
  • 原文地址:https://www.cnblogs.com/PPXppx/p/9861438.html
Copyright © 2020-2023  润新知