spfa和寻找负权环模板()

负权环:模板题目链接:https://www.luogu.com.cn/problem/P3385

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<string>
 7 #include<stack>
 8 #include<queue>
 9 #include<cstring>
10 #include<map>
11 #include<iterator>
12 #include<list>
13 #include<set>
14 #include<functional>
15 #include<memory.h>//手滑复制的那么多
16 
17 using namespace std;
18 
19 const int maxn=5005;//顶点最大数
20 const int M=5005;//边最大数
21 const int inf=0x3f3f3f3f;
22 int n,m;//顶点数与边数。
23 int tot;//递增序号,用来存储。
24 int cnt[maxn];//cnt[i]记录的是i顶点的入队次数。若大于n,则必然存在负环。
25 typedef struct Edge{
26     int next;//当前边的下一条边。
27     int to;//边的终端节点
28     int w;//边权值。
29 }Edge;
30 Edge edge[M];//用边来表示图。
31 int head[maxn];//head[i]表示以i为起点的第一条边的编号,若为-1则表示以该起点没有边。
32 int dis[maxn];//存储所有顶点到源点的距离。
33 bool visited[maxn];//判断是否在队列中。
34 void init(){//初始化 
35     memset(dis,inf,sizeof(dis));//距离初始化 
36     memset(visited,false,sizeof(visited));//初始没有访问过 
37     memset(head,-1,sizeof(head));//head就是最后一个起点为i的边的下表,在链式前向星里就是next 
38     memset(cnt,0,sizeof(cnt));//计数器 
39     tot=0;
40 }
41 void add(int u,int v,int w){//建图 
42     edge[tot].to=v;
43     edge[tot].w=w;
44     edge[tot].next=head[u];//插在第一条边前面。
45     head[u]=tot++;//改变第一条边的编号。
46     //因为要从0开始,所以不用++tot,初始化为0 
47 }
48 int spfa(int S){//S为起点,其实直接写1也没事 
49     queue<int> q;
50     dis[S]=0;
51     int temp;
52     q.push(S);
53     cnt[S]++;//起点计数器+1 
54     visited[S]=true;
55     int flag=0;//标志位,判断是否存在负环。
56     while(!q.empty()){
57         temp=q.front();
58         q.pop();
59         visited[temp]=false;
60         int v,w;
61         for(int i=head[temp];i!=-1;i=edge[i].next){//倒着遍历,因为初始head为-1 
62             v=edge[i].to;//保存边的终端节点。
63             w=edge[i].w;//保存边权值。
64             if(dis[v]>dis[temp]+w){
65                 //松弛操作。
66                 dis[v]=dis[temp]+w;
67                 if(!visited[v]){
68                     q.push(v);
69                     cnt[v]++;//每次松弛入队就++ 
70                     if(cnt[v]>n){flag=1;return flag;}//有负环 
71                     visited[v]=true;
72                 }
73             }
74         }
75     }
76     return flag;
77 }
78 int main(){
79     int t; 
80     cin >> t;
81     while(t--){//t个数据 
82         cin >> n >> m;//n个点,m条边 
83         int u,v,w;
84         init();//初始化 
85         for(int i=0;i<m;i++){
86             cin>>u>>v>>w;
87             add(u,v,w);
88             if(w>=0)
89                 add(v,u,w);
90         }
91         int S=1;//起点与终点。
92         int result=spfa(S);
93         if(result)cout<<"YES"<<endl;
94         else cout<<"NO"<<endl;
95     }
96     return 0;
97 }

 vector链接表做法

 1 #include<bits/stdc++.h>
 2 #define inf 2147483647
 3 
 4 using namespace std;
 5 
 6 int n , m , s;
 7 int vis[10005],d[10005];
 8 struct edge{
 9     int v;
10     int w;
11 };
12 vector<edge>adj[10005]; //开一个vector动态数组 ,注意这是有向图 
13 
14 void spfa()
15 {
16     for(int i = 1 ; i <= n; i ++)
17         d[i] = inf;//距离都赋值为最大值,以后松弛替换 
18     queue<int>q;
19     q.push(s);
20     vis[s] = 1;
21     d[s] = 0;
22     while(!q.empty())
23     {
24         int u = q.front();
25         q.pop();
26         vis[u] = 0;
27         for(int i = 0 ; i < adj[u].size(); i ++)//vector可以理解为1个竖着的数组
28         //如果一个点存在多个出度,可以在从这横向延申,size()便是的长度 
29         {//遍历每条边 
30             int v = adj[u][i].v;
31             int w = adj[u][i].w;
32             if(d[u]+w<d[v])//松弛 
33             {
34                 d[v] = d[u]+w;
35                 if(vis[v]==0)
36                 {
37                     vis[v]=1;
38                     q.push(v);
39                 }
40             }
41         }
42     }
43 }
44 
45 int main()
46 {
47     scanf("%d%d%d",&n,&m,&s);//n个点,m条边,出发点为s 
48     for(int i = 1 ; i <= m ; i ++)
49     {
50         int u,v,w;
51         scanf("%d%d%d",&u,&v,&w);
52         edge a;
53         a.v = v;
54         a.w = w; 
55         adj[u].push_back(a);//存边,只用1个,无向图再加一个 
56     }
57     spfa();
58     for(int i = 1 ; i <= n ; i ++)
59     {
60         cout<<d[i]<<" ";//输入从s到每个点的最短路 
61     }
62 } 
————————

负权环:模板题目链接:https://www.luogu.com.cn/problem/P3385

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<string>
 7 #include<stack>
 8 #include<queue>
 9 #include<cstring>
10 #include<map>
11 #include<iterator>
12 #include<list>
13 #include<set>
14 #include<functional>
15 #include<memory.h>//手滑复制的那么多
16 
17 using namespace std;
18 
19 const int maxn=5005;//顶点最大数
20 const int M=5005;//边最大数
21 const int inf=0x3f3f3f3f;
22 int n,m;//顶点数与边数。
23 int tot;//递增序号,用来存储。
24 int cnt[maxn];//cnt[i]记录的是i顶点的入队次数。若大于n,则必然存在负环。
25 typedef struct Edge{
26     int next;//当前边的下一条边。
27     int to;//边的终端节点
28     int w;//边权值。
29 }Edge;
30 Edge edge[M];//用边来表示图。
31 int head[maxn];//head[i]表示以i为起点的第一条边的编号,若为-1则表示以该起点没有边。
32 int dis[maxn];//存储所有顶点到源点的距离。
33 bool visited[maxn];//判断是否在队列中。
34 void init(){//初始化 
35     memset(dis,inf,sizeof(dis));//距离初始化 
36     memset(visited,false,sizeof(visited));//初始没有访问过 
37     memset(head,-1,sizeof(head));//head就是最后一个起点为i的边的下表,在链式前向星里就是next 
38     memset(cnt,0,sizeof(cnt));//计数器 
39     tot=0;
40 }
41 void add(int u,int v,int w){//建图 
42     edge[tot].to=v;
43     edge[tot].w=w;
44     edge[tot].next=head[u];//插在第一条边前面。
45     head[u]=tot++;//改变第一条边的编号。
46     //因为要从0开始,所以不用++tot,初始化为0 
47 }
48 int spfa(int S){//S为起点,其实直接写1也没事 
49     queue<int> q;
50     dis[S]=0;
51     int temp;
52     q.push(S);
53     cnt[S]++;//起点计数器+1 
54     visited[S]=true;
55     int flag=0;//标志位,判断是否存在负环。
56     while(!q.empty()){
57         temp=q.front();
58         q.pop();
59         visited[temp]=false;
60         int v,w;
61         for(int i=head[temp];i!=-1;i=edge[i].next){//倒着遍历,因为初始head为-1 
62             v=edge[i].to;//保存边的终端节点。
63             w=edge[i].w;//保存边权值。
64             if(dis[v]>dis[temp]+w){
65                 //松弛操作。
66                 dis[v]=dis[temp]+w;
67                 if(!visited[v]){
68                     q.push(v);
69                     cnt[v]++;//每次松弛入队就++ 
70                     if(cnt[v]>n){flag=1;return flag;}//有负环 
71                     visited[v]=true;
72                 }
73             }
74         }
75     }
76     return flag;
77 }
78 int main(){
79     int t; 
80     cin >> t;
81     while(t--){//t个数据 
82         cin >> n >> m;//n个点,m条边 
83         int u,v,w;
84         init();//初始化 
85         for(int i=0;i<m;i++){
86             cin>>u>>v>>w;
87             add(u,v,w);
88             if(w>=0)
89                 add(v,u,w);
90         }
91         int S=1;//起点与终点。
92         int result=spfa(S);
93         if(result)cout<<"YES"<<endl;
94         else cout<<"NO"<<endl;
95     }
96     return 0;
97 }

 vector链接表做法

 1 #include<bits/stdc++.h>
 2 #define inf 2147483647
 3 
 4 using namespace std;
 5 
 6 int n , m , s;
 7 int vis[10005],d[10005];
 8 struct edge{
 9     int v;
10     int w;
11 };
12 vector<edge>adj[10005]; //开一个vector动态数组 ,注意这是有向图 
13 
14 void spfa()
15 {
16     for(int i = 1 ; i <= n; i ++)
17         d[i] = inf;//距离都赋值为最大值,以后松弛替换 
18     queue<int>q;
19     q.push(s);
20     vis[s] = 1;
21     d[s] = 0;
22     while(!q.empty())
23     {
24         int u = q.front();
25         q.pop();
26         vis[u] = 0;
27         for(int i = 0 ; i < adj[u].size(); i ++)//vector可以理解为1个竖着的数组
28         //如果一个点存在多个出度,可以在从这横向延申,size()便是的长度 
29         {//遍历每条边 
30             int v = adj[u][i].v;
31             int w = adj[u][i].w;
32             if(d[u]+w<d[v])//松弛 
33             {
34                 d[v] = d[u]+w;
35                 if(vis[v]==0)
36                 {
37                     vis[v]=1;
38                     q.push(v);
39                 }
40             }
41         }
42     }
43 }
44 
45 int main()
46 {
47     scanf("%d%d%d",&n,&m,&s);//n个点,m条边,出发点为s 
48     for(int i = 1 ; i <= m ; i ++)
49     {
50         int u,v,w;
51         scanf("%d%d%d",&u,&v,&w);
52         edge a;
53         a.v = v;
54         a.w = w; 
55         adj[u].push_back(a);//存边,只用1个,无向图再加一个 
56     }
57     spfa();
58     for(int i = 1 ; i <= n ; i ++)
59     {
60         cout<<d[i]<<" ";//输入从s到每个点的最短路 
61     }
62 }