最短路算法—SPFA算法
SPFA算法 最坏时间复杂度为:\(O(m*n)\).一般为.\(O(m)\)
该算法与堆优化版本的Dijkstra算法很像,但相较与Dijkstra算法每次松弛,SPFA算法仅仅只会松弛更新的最小点与其相关的边。
SPFA算法思想:
每一次松弛可以更新的边,然后将其点加入队列中等待下一次松弛,直到已经松弛完所有的点或者无松弛的边。
下面给出SPFA算法的模板。
SPFA算法的优势:
最佳的求出最短路算法——方便易懂好写
判定是否存在负环——超级源点思想
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int spfa()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
queue<int> q;
q.push(1);
st[1] = true; //st数组存储的是:当前点是否在队列之中;
/*BFS + 贪心 = SPFA*/
while(q.size()){
int t = q.front();
q.pop();
st[t] = false;
for(int i = h[t]; i != -1; i = ne[i]){
int j = e[i];
if(dist[j] > dist[t] + w[i]){
dist[j] = dist[t] + w[i];
if(!st[j]){
q.push(j);
st[j] = true;
}
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
int ret = spfa();
printf("%d \n",ret);
return 0;
}
spfa判断负环,如果存在一个最短路的边长大于等于n的话,说明存在负环。
证明:如果不存在负环,对于一个连通图来说,那么一定存在n-1条道路就可以从点\(A\)抵达点\(B\),而现在存在边数为\(n\)的最短路,说明一定存在负环。
此份代码中dist的意为:到dist[i]的最短路为多少。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int h[N],e[N],w[N],ne[N],idx;
bool st[N];
int dist[N];
int cnt[N];
int n,m;
void add(int a,int b,int c){
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
bool spfa(){
queue<int> q;
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 u = e[i];
if(dist[u] > dist[t] + w[i]){
dist[u] = dist[t] + w[i];
cnt[u] = cnt[t] + 1;
if(cnt[u] >= n) return true;
if(!st[u]){
q.push(u);
st[u] = true;
}
}
}
}
return false;
}
int main(){
scanf("%d%d",&n,&m);
memset(h , -1, sizeof(h));
while(m--){
int a , b , c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
if(spfa()) printf("Yes");
else printf("No");
return 0;
}