【洛谷 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\n");
64       else printf("N0\n");
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\n");
53       else printf("N0\n");
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\n");
62       else printf("N0\n");
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\n");
62       else printf("N0\n");
63     }
64     return 0;
65 }
dfs AC

 

posted @ 2016-11-18 08:54  konjac蒟蒻  阅读(195)  评论(0编辑  收藏  举报