spfa算法
acwing 851: spfa求最短路
给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出impossible。
数据保证不存在负权回路。
输入格式
第一行包含整数n和m。
接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。
输出格式
输出一个整数,表示1号点到n号点的最短距离。
如果路径不存在,则输出”impossible”。
数据范围
1≤n,m≤105,
图中涉及边长绝对值均不超过10000。
输入样例:
3 3
1 2 5
2 3 -3
1 3 4
输出样例:
2
#include <iostream> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int N = 100010; int n, m, e[N], d[N], h[N], ne[N], idx; int w[N]; bool st[N]; void add(int a, int b, int c) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } int spfa() { memset(d, 0x3f, sizeof d); d[1] = 0; queue<int> q; q.push(1); st[1] = true; while(!q.empty()) { int t = q.front(); q.pop(); st[t] = false; for(int i = h[t] ; i != -1; i = ne[i]) { int j = e[i]; if(d[j] > d[t] + w[i]){ d[j] = d[t] + w[i]; if(!st[j]) { q.push(j); st[j] = true; } } } } if(d[n] == 0x3f3f3f3f) return -1;//这里不需要像bellman_ford里面一样/2,因为无穷大的边不会更新加入到队列当中来。 return d[n]; } int main() { cin>>n>>m; memset(h, -1, sizeof h); for(int i = 0;i<m;i++) { int x, y, z; cin>>x>>y>>z; add(x, y, z); } int t = spfa(); if(t == -1) cout<<"impossible"<<endl; else cout<<t<<endl; }
spfa是bellman_ford算法的改进,不会无脑的所有一把更新,而是更新成距离短的,和Dijkstra长得很像,都是用队列,两种算法的对比:
可以看到Dijkstra采用的优先队列,最小堆,每次得到的队首元素都是连接当中最小的,然后进过一次就不允许再进了,但是spfa可以允许多次入队。
852. spfa判断负环
给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你判断图中是否存在负权回路。
输入格式
第一行包含整数n和m。
接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。
输出格式
如果图中存在负权回路,则输出“Yes”,否则输出“No”。
数据范围
1≤n≤20001≤n≤2000,
1≤m≤100001≤m≤10000,
图中涉及边长绝对值均不超过10000。
输入样例:
3 3
1 2 -1
2 3 4
3 1 -4
输出样例:
Yes
#include <iostream> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int N = 2001, M = 10001; int w[M], e[M], d[N], h[N], ne[M], cnt[N]; int n, m, idx; bool st[N]; void add(int a, int b, int c) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } int spfa() { // memset(d, 0x3f, sizeof d); // d[1] = 0; //找的不是最短距离,不需要初始化为无穷大和1号节点 queue<int> q; // q.push(1); //判断存不存在负环,但不是判断从1开始的,1可能到不了n号点,把所有的点放进来,只要存在负环就一定可以找到 // st[1] = true; for(int i = 1;i<=n;i++) { st[i] = true; q.push(i); } while(!q.empty()) { int t = q.front(); q.pop(); st[t] = false; for(int i = h[t] ; i != -1; i = ne[i]) { int j = e[i]; if(d[j] > d[t] + w[i]){ d[j] = d[t] + w[i]; cnt[j] = cnt[t] + 1; if(cnt[j] >= n) return 1; if(!st[j]) { q.push(j); st[j] = true; } } } } // if(d[n] == 0x3f3f3f3f) return -1;//这里不需要像bellman_ford里面一样/2,因为无穷大的边不会更新加入到队列当中来。 return 0; } int main() { cin>>n>>m; memset(h, -1, sizeof h); for(int i = 0;i<m;i++) { int x, y, z; cin>>x>>y>>z; add(x, y, z); } int t = spfa(); if(t) cout<<"Yes"<<endl; else cout<<"No"<<endl; }