模板-Bellman-Ford&SPFA

Bellman-Ford算法

求最短路的

这个算法基于一个叫做“松弛”的操作

松弛会试图往最短路中加边来缩短最短路

对于这样一个图

 1到3的最短路显然是1→2→3而不是1→3绕远路就是最短的捷径

我们所进行的松弛操作就是这样的

松弛时枚举每一条边,并判断先走最短路到达这条边的u点,再经过这条边到达v点,能否使到v点的最短路缩小

若可以,就将到v点的最短路更新

显然,只用一次松弛不能够得到完整的最短路,所以要进行多次松弛

经过一大堆乱七八糟的玄学证明,我们知道只需要进行n-1次松弛即可

所以,当n=1时,我们只要进行0次松弛即可

n=2时,1次即可

n=3,3次即可

n=2147483647,2147483646即可

不行这个数太大了

实际上我们发现,n-1是最大次数

实际上有效松弛有可能没有这么多

所以我们判断如果这次松弛没有让最短路发生变化,就直接结束算法

对于负权环,由于正常情况下最多进行n-1次有效松弛,如果在n-1次全部完成后还可以松弛,就可以说明这里面有负权环了

代码:

#include <bits/stdc++.h>
using namespace std;
int n,m;
struct Edge
{
    int u,v,w,nxt;
}e[10];
int h[10],cnt;
int dis[10];
int bak[10];
void add(int u,int v,int w)
{
    e[++cnt].u=u;
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].nxt=h[u];
    h[u]=cnt;
}
int main()
{
    cin >> n >> m;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c);
    }
    for(int i=1;i<=n;i++)
        dis[i]=0x3f;
    dis[1]=0;
    for(int k=1;k<=n-1;k++)
    {
        for(int i=1;i<=n;i++)
            bak[i]=dis[i];
        for(int i=1;i<=m;i++)
            if(dis[e[i].v]>dis[e[i].u]+e[i].w)
                dis[e[i].v]=dis[e[i].u]+e[i].w;
        int check=0;
        for(int i=1;i<=n;i++)
            if(bak[i]!=dis[i])
            {
                check=1;
                break;
            }
        if(!check)
            break;
    }
    int flag=0;
    for(int i=1;i<=m;i++)
        if(dis[e[i].v]>dis[e[i].u]+e[i].w)
            flag=1;
    if(flag==1)
        cout << "无最短路" << endl;
    else
    {
        for(int i=1;i<=n;i++)
            cout << dis[i] << ' ';
    }
    return 0;
}

但是我们回头看看这个代码

松弛时枚举每一条边

每一条

根据我的经验,我怀疑这里面有可能有许多没用的松弛

实际上我们可以通过一大堆乱七八糟的玄学证明,松弛只有可能令一部分点的最短路缩小

这一部分点就是松弛时所加入的那条边的v点所连着的点

于是我们就接触到了另一个算法,另一个在Bellman-Ford算法基础上的最短路算法——

Shortest Path Faster Algorithm

它利用队列,每次取出队首,枚举队首所有的边进行松弛

并将那些松弛成功的边的v点入队

以达到避免无效松弛的效果

代码:

#include <bits/stdc++.h>
using namespace std;
int n,m;
struct Edge
{
    int u,v,w,nxt=-1;
}e[10];
int h[10],cnt;
int dis[10];
int bak[10];
void add(int u,int v,int w)
{
    e[++cnt].u=u;
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].nxt=h[u];
    h[u]=cnt;
}
queue<int> q;
int bk[10];
int main()
{
    cin >> n >> m;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c);
    }
    for(int i=1;i<=n;i++)
        dis[i]=999999;
    dis[1]=0;
    for(int i=1;i<=n;i++)
        bk[i]=0;
    q.push(1);
    bk[1]=1;
    while(!q.empty())
    {
        int k=h[q.front()];
        while(k!=-1)
        {
            if(dis[e[k].v]>dis[e[k].u]+e[k].w)
            {
                dis[e[k].v]=dis[e[k].u]+e[k].w;
                if(bk[e[k].v]==0)
                {
                    q.push(e[k].v);
                    bk[e[k].v]=1;
                }
            }
            k=e[k].nxt;
        }
        bk[q.front()]=0;
        q.pop();
    }
    for(int i=1;i<=n;i++)
        cout << dis[i] << ' ';
    return 0;
}

  

原文地址:https://www.cnblogs.com/lujin49/p/13516612.html