SPFA算法

(本文中的图片来源于博友的博客,图中有水印)

作用:找最短路径

说明:是dijkstra的优化,动态寻找各点的最短路径,尤其可以处理有向负权值

 

算法:

1.选取一个起点S,设定一个记录S到其它所有点距离的数组并初始化,操作如下:

①设定地图

②设定距离数组(S,S)=0,(S,Ki≠S)=inf

③设定处理队列{S}

2.对队列中的每一个点Ki进行以下操作:

①根据它所有的可行路径更新距离数组,更新过程中注意:

     (1)如果目标点已经在队列中,不再入列,否则入列

      (2)比较(S,Ki)+(Ki,Kj)和(S,Kj),记录较小的值

②弹出当前处理点Ki,检查队列中是否还有元素,如果有,处理下一个队首元素(步骤①),否则结束

3.距离数组中的值就是需要的S到各点的最短路程

 

 

操作:

如图,我们要找这个图的v1到其它点的最短路径

SPFA算法

初始的数组记录如下:

SPFA算法

处理队列:{v1}

从v1开始搜索可能的路径,更新如下:

SPFA算法

此时,v1处理完毕,出列,而v3、v5、v6没有处理,入列(有点像BFS)

处理队列:{v3,v5,v6}

接下来处理v3,v3和上述点暂时还没有关联,继续更新:

SPFA算法

处理队列:{v5,v6,v4}

然后处理v5,v5的处理涉及到v4和v6,当然也涉及到v1最短路径可能要修改

v5到v6的距离是60,而v4和v6在表中已经有值了,这时就要多一个操作,那就是比较(v1,v5)+(v5,vi)和表中已经存储的(v1,vi),将更短的哪一个存下来,于是更新如下:

SPFA算法

上一步操作涉及到了v4和v6,它们已经在队列中,不需要重复入列

处理队列:{v6,v4}

再处理v6,没有可走路径,直接出列

处理队列:{v4}

v4到v6长度为10,比较10+50和90,发现(v1,v4,v6)是一个更好的选择,更新如下:

SPFA算法

v6被更新了,所以v4出列,v6入列!

处理队列:{v6}

同样的,v6没有路可走,那么直接出列,队列空了,v1为源的遍历完成。其中v2不可达。

 

实战:

【题目一】

https://www.luogu.org/problemnew/show/P2683

#include<iostream>//long long不能做数组下标 
#include<cstdio>
#include<cstring>
using namespace std;
int n,m;
int u,v,d;
int s,e;//起点 终点 
int pd;
int exist[1000];//记录是否入队 
int team[10000001];//队列 
int dis[1001];//最短路 
int g[101][101];//邻接矩阵存图 
int head,tail;
int spfa()
{
    memset(dis,0x7f,sizeof(dis));
    dis[s]=0;
    memset(exist,false,sizeof(exist));
    team[1]=s;
    head=0;
    tail=1;
    exist[s]=true;//以上为spfa初始化 
    do
    {
        ++head;
        u=team[head];
        exist[u]=false;//取出一个点 
        for(v=1;v<=n;++v)
        {
            if(dis[v]>dis[u]+g[u][v])
            {
                dis[v]=dis[u]+g[u][v];//松弛操作,更新 
                if(!exist[v])
                {
                    ++tail;
                    team[tail]=v;
                    exist[v]=true;//如果被更新的点没在队列里就入队 
                } 
            }
        }
    }while(head<tail);
}
int main()
{
    cin>>n>>m;
    memset(g,0x7f,sizeof(g));//邻接矩阵初始化 
    for(int i=1;i<=m;++i)
    {
        cin>>pd;
        if(pd==1)
        {
            cin>>u>>v>>d;
            if(d<g[u][v])
            {
                g[u][v]=g[v][u]=d;//更新边的信息 
            }
        }
        else
        {
            cin>>s>>e;
            spfa();//求s到e的最短路 
            if(dis[e]>10000000) cout<<-1<<endl;//如果s到e的距离太大则不存在路 
            else cout<<dis[e]<<endl;
        }
    }
    return 0;
}

 

相关文章: