zoukankan      html  css  js  c++  java
  • 【洛谷 P3385】模板-负环(图论--spfa)

    题目:有一个图有N个顶点,M条边。边用三个整数a b w表示,意思为a->b有一条权值为w的边(若w<0则为单向,否则双向)。共T组数据。对于每组数据,存在负环则输出一行"YE5"(不含引号),否则输出一行"N0"(不含引号)。

    注意——坑爹的输出啊!!它不是平常的 YES 和 NO!!

    解法:1.spfa_bfs,判断结点入队超过 n 次就出现负环。最差的情况是O(nm)。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 #include<queue>
     6 using namespace std;
     7 
     8 const int N=200010;
     9 struct edge{int x,y,d,next;}a[N*2];
    10 int last[N],vis[N],cnt[N],d[N];
    11 int len,n,m;
    12 queue<int> q;
    13 
    14 void ins(int x,int y,int d)
    15 {
    16     a[++len].x=x,a[len].y=y,a[len].d=d;
    17     a[len].next=last[x],last[x]=len;
    18 }
    19 bool spfa()
    20 {
    21     while (!q.empty()) q.pop();
    22     memset(d,63,sizeof(d));
    23     memset(cnt,0,sizeof(cnt));
    24     memset(vis,0,sizeof(vis));
    25     d[1]=0,vis[1]=1,cnt[1]++;
    26     q.push(1);
    27     while (!q.empty())
    28     {
    29       int x=q.front();
    30       q.pop(); vis[x]=0;//此处无cnt++
    31       for (int i=last[x];i!=-1;i=a[i].next)
    32       {
    33         int y=a[i].y;
    34         if (d[x]+a[i].d<d[y])
    35         {
    36           d[y]=d[x]+a[i].d;
    37           if (!vis[y])
    38           {
    39             q.push(y);
    40             vis[y]=1, cnt[y]++;
    41             if (cnt[y]>n) return true;
    42           }
    43         }
    44       }
    45     }
    46     return false;
    47 }
    48 int main()
    49 {
    50     int T;
    51     scanf("%d",&T);
    52     while (T--)
    53     {
    54       scanf("%d%d",&n,&m);
    55       int x,y,d; len=0;
    56       memset(last,-1,sizeof(last));
    57       for (int i=1;i<=m;i++)
    58       {
    59         scanf("%d%d%d",&x,&y,&d);
    60         ins(x,y,d);
    61         if (d>=0) ins(y,x,d);
    62       }
    63       if (spfa()) printf("YE5
    ");
    64       else printf("N0
    ");
    65     }
    66     return 0;
    67 }
    bfs TLE

             2.spfa_dfs,枚举起点,找最短路,若再一次访问到已经访问过的结点就出现负环。最差的情况也是O(nm)。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 using namespace std;
     6 
     7 const int N=200010,INF=(int)1e9;
     8 struct edge{int x,y,d,next;}a[N*2];
     9 int last[N],vis[N],dis[N];
    10 int len,n,m;
    11 
    12 void ins(int x,int y,int d)
    13 {
    14     a[++len].x=x,a[len].y=y,a[len].d=d;
    15     a[len].next=last[x],last[x]=len;
    16 }
    17 bool dfs(int x)
    18 {
    19     if (vis[x]) return true;
    20     vis[x]=1;
    21     for (int i=last[x];i!=-1;i=a[i].next)
    22     {
    23       int y=a[i].y;
    24       //if (y==fa) continue;//有dis的判断
    25       if (dis[x]+a[i].d<dis[y])
    26       {
    27         dis[y]=dis[x]+a[i].d;
    28         if (dfs(y)) return true;
    29       }
    30     }
    31     vis[x]=0;//判断一个点是否在“同一”路径重复出现而已。
    32     return false;//
    33 }
    34 int main()
    35 {
    36     int T;
    37     scanf("%d",&T);
    38     while (T--)
    39     {
    40       scanf("%d%d",&n,&m);
    41       int x,y,d; len=0;
    42       memset(last,-1,sizeof(last));
    43       for (int i=1;i<=m;i++)
    44       {
    45         scanf("%d%d%d",&x,&y,&d);
    46         ins(x,y,d);
    47         if (d>=0) ins(y,x,d);
    48       }
    49       for (int i=1;i<=n;i++) dis[i]=INF;
    50       memset(vis,0,sizeof(vis));
    51       dis[1]=0;//起点不确定的!
    52       if (dfs(1)) printf("YE5
    ");
    53       else printf("N0
    ");
    54     }
    55     return 0;
    56 }
    dfs WA(起点不能只是随便定一个点)
     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 using namespace std;
     6 typedef long long LL;
     7 
     8 const int N=200010,D=200010;
     9 const LL INF=(LL)5e10;
    10 int n,m,len;
    11 bool cir;
    12 int last[N],vis[N];
    13 LL dis[N];
    14 struct edge{int x,y,next;LL d;}a[2*N];
    15 
    16 void ins(int x,int y,LL d)
    17 {
    18     a[++len].x=x,a[len].y=y,a[len].d=d;
    19     a[len].next=last[x],last[x]=len;
    20 }
    21 void dfs(int x)
    22 {
    23     if (vis[x]) {cir=true;return;}
    24     vis[x]=1;
    25     for (int i=last[x];i;i=a[i].next)
    26     {
    27       int y=a[i].y;
    28       if (dis[x]+a[i].d<dis[y])
    29       {
    30         dis[y]=dis[x]+a[i].d;
    31         dfs(y);
    32         if (cir) return;
    33       }
    34     }
    35     vis[x]=0;
    36 }
    37 int main()
    38 {
    39     int T;
    40     scanf("%d",&T);
    41     while (T--)
    42     {
    43       int x,y; LL d;
    44       len=0;
    45       memset(last,0,sizeof(last));
    46       scanf("%d%d",&n,&m);
    47       for (int i=1;i<=m;i++)
    48       {
    49         scanf("%d%d%lld",&x,&y,&d);
    50         ins(x,y,d);
    51         if (d>=0) ins(y,x,d);
    52       }
    53       cir=false;
    54       for (int i=1;i<=n;i++)
    55       {
    56         for (int j=1;j<=n;j++) dis[j]=INF;
    57         memset(vis,0,sizeof(vis));
    58         dis[i]=0, dfs(i);
    59         if (cir) break;
    60       }
    61       if (cir) printf("YE5
    ");
    62       else printf("N0
    ");
    63     }
    64     return 0;
    65 }
    dfs TLE

             3.spfa_dfs+优化,既然是找负环,那么就是找权和为负数的回路。P.S.这个一定要理解!!ヾ(。 ̄□ ̄)ツ゜゜゜我想了很久......网上也没搜到。负环负环,不单单是含负权边的环,而是权和为负的环。原因是对于一个在负环里的点,只有经过负环后的距离还比原来的小,才会再一次访问该点。要不怎么会一直循环走这个环,直到RE呢......ヘ(_ _ヘ) 也就是环的权和为负数!
         实现就是我们对 dis 数组清零,再权和为负的情况下做spfa,且使用 dfs 判断成环。时间复杂度远远小于O(nm)。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 using namespace std;
     6 typedef long long LL;
     7 
     8 const int N=200010,D=200010;
     9 const LL INF=(LL)5e10;
    10 int n,m,len;
    11 bool cir;
    12 int last[N],vis[N];
    13 LL dis[N];
    14 struct edge{int x,y,next;LL d;}a[2*N];
    15 
    16 void ins(int x,int y,LL d)
    17 {
    18     a[++len].x=x,a[len].y=y,a[len].d=d;
    19     a[len].next=last[x],last[x]=len;
    20 }
    21 void dfs(int x)
    22 {
    23     if (vis[x]) {cir=true;return;}
    24     vis[x]=1;
    25     for (int i=last[x];i;i=a[i].next)
    26     {
    27       int y=a[i].y;
    28       if (dis[x]+a[i].d<dis[y])
    29       {
    30         dis[y]=dis[x]+a[i].d;
    31         dfs(y);
    32         if (cir) return;
    33       }
    34     }
    35     vis[x]=0;
    36 }
    37 int main()
    38 {
    39     int T;
    40     scanf("%d",&T);
    41     while (T--)
    42     {
    43       int x,y; LL d;
    44       len=0;
    45       memset(last,0,sizeof(last));
    46       scanf("%d%d",&n,&m);
    47       for (int i=1;i<=m;i++)
    48       {
    49         scanf("%d%d%lld",&x,&y,&d);
    50         ins(x,y,d);
    51         if (d>=0) ins(y,x,d);
    52       }
    53       memset(dis,0,sizeof(dis));
    54       memset(vis,0,sizeof(vis));
    55       cir=false;
    56       for (int i=1;i<=n;i++)
    57       {
    58         dfs(i);
    59         if (cir) break;
    60       }
    61       if (cir) printf("YE5
    ");
    62       else printf("N0
    ");
    63     }
    64     return 0;
    65 }
    dfs AC
  • 相关阅读:
    监控系统
    RocketMQ入门介绍
    Linux的虚拟内存详解(MMU、页表结构) 转
    快速排序
    如何选择分布式事务解决方案? 转
    java 基本数据类型相关思考
    互联网项目中mysql应该选什么事务隔离级别 转
    线上服务的FGC问题排查,看这篇就够了! 转
    什么是Base64? 转
    业界难题-“跨库分页”的四种方案 转
  • 原文地址:https://www.cnblogs.com/konjak/p/6076308.html
Copyright © 2011-2022 走看看