概述:
SPFA–Shortest Path Faster Algorithm
是一种单源最短路算法,通常被认为是Bellman-ford算法的队列优化,在代码形式上接近于BFS,是一个实际中一个非常高效的算法,并且可以解决Dijkstra算法中的所不能解决的负边权,但是如果竞赛中有出题人出数据卡你的spfa,你就难受了,所以这里我建议没有负边权,不要轻易使用spfa算法,而是使用优先队列优化的Dijkstra算法。
关于spfa卡数据的文章大家可以百度搜spfa卡网格图数据。
这里不过多赘述。
1.SPFA算法简介
2.SPFA思想:
时间复杂度:
SPFA空间效率一般为O(V)。如果顶点的平均入队次数为k次,则SPFA的时间复杂度为O(kE),对于较为随机的稀疏图,根据经验k一般不超过4.
3.SPFA优化:
4.SPFA算法过程:
说了那么多,它是怎么实现的呢,
我们的源点是0,因为是有向图
a.0点能到1, 2, 5,所以将0点标记已经访问过然后我们取队首元素为0,将标记取消掉(因为每个点可能存在多次访问),然后我们将1入队,判断dis[0] + w < dis[1] 发现小于,就更新dis[1],然后发现0还连着的边的点还有2和5我们重复上述操作更新dis[2]、dis[5]。
b.然后这时候队里面还剩下1、2、5三个元素,取出队首然元素1,找到1能访问的点2然后更新dis[2]发现2没被访问加入到队中,然后重复这些操作直到队为空就将最短路求出。
c.有点类似与BFS的思想,只不过bfs能保证第一次访问就是最短路,而spfa在每次更新了最短路以后又重新入队从而去更新后续结点的最短路。
5.Code:
bool inq[MAX_N];
int d[MAX_N]; // 如果到顶点 i 的距离是 0x3f3f3f3f,则说明不存在源点到 i 的最短路
void spfa(int s) {
memset(inq, 0, sizeof(inq));
memset(d, 0x3f, sizeof(d));
d[s] = 0;
inq[s] = true;
queue<int> q;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
inq[u] = false;
for (int i = p[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if (d[u] + e[i].w < d[v]) {
d[v] = d[u] + e[i].w;
if (!inq[v]) {
q.push(v);
inq[v] = true;
}
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
6.SPFA判断负环:
其实SPFA算法不是万能的,当图中存在负环的时候便不能通过SPFA算法进行计算最短路。
实现:
Code:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e3 + 9;
const int M = 1e4 + 9;
struct edge {
int v, w, fail;
edge() {}
edge(int _v, int _w, int _fail) {
v = _v;
w = _w;
fail = _fail;
}
} e[M << 1];
int head[N], len;
void init() {
memset(head, -1, sizeof(head));
len = 0;
}
void add(int u, int v, int w) {
e[len] = edge(v, w, head[u]);
head[u] = len++;
}
void add2(int u, int v, int w) {
add(u, v, w);
add(v, u, w);
}
int n, m;
int dis[N], in[N]; //in代表不同顶点入队的次数
bool vis[N];
bool spfa(int u) {
memset(vis, false, sizeof(vis));
vis[u] = true;
memset(dis, 0x3f, sizeof(dis));
dis[u] = 0;
memset(in, 0, sizeof in);
in[u] = 1;
queue<int> q;
q.push(u);
while (!q.empty()) {
u = q.front();
q.pop();
vis[u] = false;
for (int j = head[u]; ~j; j = e[j].fail) {
int v = e[j].v;
int w = e[j].w;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (!vis[v]) {
q.push(v);
vis[v] = true;
++in[v];
if (in[v] > n) {
return true;
}
}
}
}
}
return false;
}
int main() {
init();
int u, v, w;
cin >> n >> m;
while (m--) {
cin >> u >> v >> w;
add2(u, v, w);
}
if (spfa(1)) {
cout << "yes" << endl;
} else {
cout << "no" << endl;
}
return 0;
}