最短路(2)--bellman-ford和SPFA

bellman-ford

首先,如果最短路存在,那么一定有一条不含环的最短路,因为如果是正环或零环,都可以直接去除,如果有负环,则最短路不存在,所以最短路顶多经过n-1个顶点,那么我们至多只需要进行n-1次松弛操作,每次操作中遍历所有边,如果该边的起点不是INF(已经松弛过)那么就对该边的终点松弛。这样就一定可以把最短路经过的所有点都松弛一遍,即求出了最短路(因为只要可以松弛,就一定有更短的方案)。

//bellmanford
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 1000 + 10;
const int INF = 0x3f3f3f3f;
int u[MAXN], v[MAXN], w[MAXN];
int d[MAXN];
int n, m;

int main()
{
    freopen("sp.in", "r", stdin);
    cin >> n >> m;
    for(int i = 1; i <= m; i++)
    {
        cin >> u[i] >> v[i] >> w[i];
        u[i+m] = v[i], v[i+m] = u[i], w[i+m] = w[i];
    }

    d[1] = 0;
    for(int i = 2; i <= n; i++) d[i] = INF;

    for(int k = 1; k < n; k++)
        for(int i = 1; i <= 2*m; i++)
            if(d[u[i]] < INF) d[v[i]] = min(d[v[i]], d[u[i]] + w[i]);

    for(int i = 1; i <= n; i++)
        cout << d[i] << " ";
    return 0;
}

SPFA

思路:

  1. 把除起点外的所有点距离设为无限,然后让起点进入队列
  2. 每次取出队列的第一个点,讨论他的所有只向点进行松弛操作,如果松弛成功并且该点并不在队列中,将该点放入队列,直到队列中没有点为止

和dijkstra的比较

  • dijkstra每次取出的是当前距离最小点,在后面的讨论中不可能再更新该点的值,所以每个点只用讨论一次
  • SPFA每次取出的只是一个松弛过的点,很有可能再次讨论回来,所以一个点要讨论多次
  • 但是因为dijkstra中每次找距离最小点需要时间复杂度,所以O(mlogn)
  • 而SPFA没有这个需要,所以O(kn),k是常数,而一般不会超过2
  • 而且dijkstra只能解决没有负权的问题,而SPFA可以解决,但不能解决有负环的问题,因为有负环的图没有最短路,他还可以判定负环,当一个点的进队次数超过n后,可以判定图中有负环

下面是SPFA的代码

//SPFA
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 10000 + 10;
const int INF = 0x3f3f3f3f;
queue<int> q;
int n, m;
int d[MAXN];
int vis[MAXN];

struct edge
{
    int to, next, w;
}e[MAXN]; 
int head[MAXN], cnt;

void add(int u, int v, int w)
{
    e[++cnt].next = head[u];
    head[u] = cnt;
    e[cnt].to = v;
    e[cnt].w = w;
}

int main()
{
    freopen("sp.in", "r", stdin);
    cin >> n >> m;
    for(int i = 1; i <= m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w); add(v, u, w);
    }

    for(int i = 2; i <= n; i++)
        d[i] = INF;
    q.push(1); vis[1] = 1;

    while(!q.empty())
    {
        int u = q.front(); q.pop(); vis[u] = 0;
        for(int i = head[u]; i; i = e[i].next)
        {
            int v = e[i].to;
            if(d[v] > d[u] + e[i].w) 
            {
                d[v] = d[u] + e[i].w;
                if(!vis[v]) {q.push(v); vis[v] = 1;}
            }
        }
    }

    for(int i = 1; i <= n; i++)
        cout << d[i] << " ";
    return 0;
}
    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/rerisingstar/article/details/71792157
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞