图论最短路算法——spfa算法

概述:

SPFA–Shortest Path Faster Algorithm
是一种单源最短路算法,通常被认为是Bellman-ford算法的队列优化,在代码形式上接近于BFS,是一个实际中一个非常高效的算法,并且可以解决Dijkstra算法中的所不能解决的负边权,但是如果竞赛中有出题人出数据卡你的spfa,你就难受了,所以这里我建议没有负边权,不要轻易使用spfa算法,而是使用优先队列优化的Dijkstra算法。
关于spfa卡数据的文章大家可以百度搜spfa卡网格图数据。
这里不过多赘述。

1.SPFA算法简介

图论最短路算法——spfa算法_第1张图片

2.SPFA思想:

图论最短路算法——spfa算法_第2张图片
时间复杂度:
SPFA空间效率一般为O(V)。如果顶点的平均入队次数为k次,则SPFA的时间复杂度为O(kE),对于较为随机的稀疏图,根据经验k一般不超过4.

3.SPFA优化:

图论最短路算法——spfa算法_第3张图片

4.SPFA算法过程:

说了那么多,它是怎么实现的呢,
图论最短路算法——spfa算法_第4张图片
我们的源点是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;
                }
            }
        }
    }
}

6.SPFA判断负环:

其实SPFA算法不是万能的,当图中存在负环的时候便不能通过SPFA算法进行计算最短路。
图论最短路算法——spfa算法_第5张图片
图论最短路算法——spfa算法_第6张图片

实现:
Code:

#include 
#include 
#include 
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;
}

你可能感兴趣的:(alogrithm,图(graph),SPFA,最短路)