SPFA 算法是 Bellman-Ford算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。SPFA 最坏情况下复杂度和朴素 Bellman-Ford 相同,为 O(VE)。
判断负权边:其实就是多加一个 cnt[] 数组
1 #include <stdio.h> 2 #include <algorithm> 3 #include <iostream> 4 #include <stdbool.h> 5 #include <stdlib.h> 6 #include <string> 7 #include <string.h> 8 #include <math.h> 9 #include <vector> 10 #include <queue> 11 #include <stack> 12 #include <set> 13 #include <map> 14 15 #define INF 0x3f3f3f3f 16 #define LL long long 17 #define MAXN 1000005 18 #define mod 1000000007 19 using namespace std; 20 21 vector<pair<int,int>> E[MAXN]; 22 23 int n,m; 24 int dis[MAXN],inq[MAXN]; 25 int cnt[MAXN]; 26 27 void init() 28 { 29 for(int i=0;i<MAXN;i++) 30 E[i].clear(); 31 for(int i=0;i<MAXN;i++) 32 inq[i] = 0; 33 for(int i=0;i<MAXN;i++) 34 dis[i] = 1e9; 35 } 36 37 bool SPFA(int s) 38 { 39 queue<int> Q; 40 Q.push(s); 41 dis[s] = 0; 42 inq[s] = 1; 43 while (!Q.empty()) 44 { 45 int now = Q.front(); 46 Q.pop(); 47 inq[now] = 0; 48 for (int i=0;i<E[now].size();i++) 49 { 50 int v = E[now][i].first; 51 if (dis[v]>dis[now]+E[now][i].second) 52 { 53 dis[v] = dis[now]+E[now][i].second; 54 if (inq[v]==1) 55 continue; 56 inq[v] = 1; 57 cnt[v]++; 58 Q.push(v); 59 if (cnt[v]>m) 60 return false; 61 } 62 } 63 } 64 return true; 65 } 66 67 68 int main() 69 { 70 while (cin >> n >> m) 71 { 72 init(); 73 for (int i=0;i<m;i++) 74 { 75 int x,y,z; 76 cin >> x >> y >> z; 77 E[x].push_back(make_pair(y,z)); 78 E[y].push_back(make_pair(x,z)); 79 } 80 int s,t; 81 cin >> s >> t; 82 if(SPFA(s)) 83 printf("No"); 84 else 85 printf("Yes"); 86 } 87 return 0; 88 }
最短路径:inq[] 防止重复访问
1 #include <stdio.h> 2 #include <algorithm> 3 #include <iostream> 4 #include <stdbool.h> 5 #include <stdlib.h> 6 #include <string> 7 #include <string.h> 8 #include <math.h> 9 #include <vector> 10 #include <queue> 11 #include <stack> 12 #include <set> 13 #include <map> 14 15 #define INF 0x3f3f3f3f 16 #define LL long long 17 #define MAXN 1000005 18 #define mod 1000000007 19 using namespace std; 20 21 vector<pair<int,int>> E[MAXN]; 22 23 int n,m; 24 int dis[MAXN],inq[MAXN]; 25 26 void init() 27 { 28 for(int i=0;i<MAXN;i++) 29 E[i].clear(); 30 for(int i=0;i<MAXN;i++) 31 inq[i] = 0; 32 for(int i=0;i<MAXN;i++) 33 dis[i] = 1e9; 34 } 35 36 void SPFA(int s) 37 { 38 queue<int> Q; 39 Q.push(s); 40 dis[s] = 0; 41 inq[s] = 1; 42 while (!Q.empty()) 43 { 44 int now = Q.front(); 45 Q.pop(); 46 inq[now] = 0; 47 for (int i=0;i<E[now].size();i++) 48 { 49 int v = E[now][i].first; 50 if (dis[v]>dis[now]+E[now][i].second) 51 { 52 dis[v] = dis[now]+E[now][i].second; 53 if (inq[v]==1) 54 continue; 55 inq[v] = 1; 56 Q.push(v); 57 } 58 } 59 } 60 } 61 62 63 int main() 64 { 65 while (cin >> n >> m) 66 { 67 init(); 68 for (int i=0;i<m;i++) 69 { 70 int x,y,z; 71 cin >> x >> y >> z; 72 E[x].push_back(make_pair(y,z)); 73 E[y].push_back(make_pair(x,z)); 74 } 75 int s,t; 76 cin >> s >> t; 77 SPFA(s); 78 if (dis[t] == 1e9) 79 printf("-1\n"); 80 else 81 printf("%d\n",dis[t]); 82 } 83 return 0; 84 }
上面使用vector<pair<int,int> > 来写,现在用链表来写
其中edge[i].to表示第i条边的终点,edge[i].next表示与第i条边同起点的下一条边的存储位置,edge[i].w为边权值.
另外还有一个数组head[],它是用来表示以i为起点的第一条边存储的位置,实际上你会发现这里的第一条边存储的位置其实
在以i为起点的所有边的最后输入的那个编号.
更加具体的解释可以看博客:https://blog.csdn.net/ACdreamers/article/details/16902023
1 #include <stdio.h> 2 #include <algorithm> 3 #include <iostream> 4 #include <stdlib.h> 5 #include <math.h> 6 #include <stack> 7 #include <queue> 8 9 #define INF 0x3f3f3f3f 10 #define MAXN 10000 11 using namespace std; 12 13 struct Edge{ 14 int to; 15 int next; 16 int val; 17 }edge[MAXN*MAXN]; 18 19 int res,n; 20 int head[MAXN]; 21 bool vis[MAXN]; 22 int a[MAXN],dist[MAXN],cnt[MAXN]; 23 24 25 void init(){ 26 memset(head,-1, sizeof(head)); 27 res = 0; 28 } 29 30 void add(int u,int v,int w){ 31 edge[res].to = v; 32 edge[res].val = w; 33 edge[res].next = head[u]; 34 head[u] = res++; 35 } 36 37 bool SPFA(int st){ 38 memset(vis,false, sizeof(vis)); 39 memset(cnt,0, sizeof(cnt)); 40 for (int i=1;i<=n;i++) 41 dist[i] = INF; 42 dist[st] = 0; 43 queue<int> q; 44 q.push(st); 45 cnt[st] = 1; 46 vis[st] = true; 47 while (!q.empty()) 48 { 49 int now = q.front(); 50 q.pop(); 51 vis[now] = false; 52 for (int i=head[now];i!=-1;i=edge[i].next) 53 { 54 int v = edge[i].to; 55 if (dist[v]>dist[now]+edge[i].val) 56 { 57 dist[v] = dist[now]+edge[i].val; 58 if (!vis[v]) 59 { 60 vis[v] = true; 61 q.push(v); 62 cnt[v]++; 63 if (cnt[v] > n) 64 return false; //负环 65 } 66 } 67 } 68 } 69 }
有的题目queue的SPFA不给过,所以我们就要考虑deque双端队列的优化方法:
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <algorithm> 5 #include <vector> 6 #include <queue> 7 #include <set> 8 #include <map> 9 #include <string> 10 #include <math.h> 11 #include <stdlib.h> 12 #include <time.h> 13 using namespace std; 14 15 #define INF 0x3f3f3f3f 16 #define MAXN 100000 17 #define LL long long 18 using namespace std; 19 20 struct Edge{ 21 int to; 22 int next; 23 int val; 24 }edge[MAXN]; 25 26 27 int res; 28 int cnt[MAXN]; 29 int head[MAXN]; 30 int dist[MAXN]; 31 bool vis[MAXN]; 32 33 void init() 34 { 35 memset(head,-1, sizeof(head)); 36 res = 0; 37 } 38 39 void add(int u,int v,int w) 40 { 41 edge[res].to = v; 42 edge[res].val = w; 43 edge[res].next = head[u]; 44 head[u] = res++; 45 } 46 47 bool SPFA(int n) 48 { 49 memset(vis,false,sizeof(vis)); 50 memset(cnt,0, sizeof(cnt)); 51 for (int i=1;i<=n;i++) 52 dist[i]=INF; 53 dist[1] = 0; 54 vis[1] = true; 55 cnt[1]++; 56 deque<int > q; 57 q.push_back(1); 58 while (!q.empty()) 59 { 60 int now = q.front(); 61 q.pop_front(); 62 vis[now] = false; 63 for (int i=head[now];i!=-1;i=edge[i].next) 64 { 65 int v = edge[i].to; 66 if (dist[v]>dist[now]+edge[i].val) { 67 dist[v] = dist[now] + edge[i].val; 68 if (!vis[v]) 69 { 70 if (dist[v]<dist[q.front()] && !q.empty()) 71 q.push_front(v); 72 else 73 q.push_back(v); 74 vis[v] = true; 75 if (++cnt[v]>n) 76 return false; 77 } 78 } 79 } 80 } 81 return true; 82 } 83 84 85 86 int main() 87 { 88 int n,m; 89 while(~scanf("%d%d",&m,&n)) { 90 init(); 91 for (int i = 1; i <= m; i++) { 92 int u, v, w; 93 scanf("%d%d%d", &u, &v, &w); 94 add(u, v, w); 95 add(v, u, w); 96 } 97 SPFA(n); 98 printf("%d\n", dist[n]); 99 } 100 return 0; 101 }