zoukankan      html  css  js  c++  java
  • 图论最短路径算法——SPFA

    为了不要让太多人被害,我还是说一下这种算法,它实际上很简单,但被人讲着讲着绕晕了。

    主要思想

    有人说,SPFA是Bellman-Ford的队列优化。这个算法我也懂了,但是还没试过。我不管是什么算法的优化,反正我看着不像。
    它的思想很简单:BFS。有人说这只是类似的,并不是纯BFS。我不管这些,分这么严格干嘛呢!
    从起点开始,枚举它节点的边,走所有与它相连的路径。如果能更新别的节点就更新,不能更新嘛,就直接将它从队列里删掉,不要它了,反正它没鬼用。
    还有一点,要标记某节点是否已经在队列里,将一个节点加入时,看看它在不在队列里面,如果在,就不用放进去,反正没鬼用。
    若某个点在队列里出现过N次,则是进入了负环,赋个无限小就行了。

    实现讲解

    先讲讲图的存储。这个图并不是用邻接矩阵来弄的(你也能用,速度会慢,也许还会出现溢出等奇怪的错误)。这个东西叫邻接表,有的人也叫它边集数组。就是记录每一个节点,与它相连的所有的边。每条边的信息有这个点的另一端,已及这条边的权值。
    我是这样定义的

    struct _Way//定义_Way类型,表示某点与y点相连,长度为len 
    {
        int y,len;  
    };
    _Way way[1001][1001] {};//way[i][j]标示i的第j条路 
    int now[1001] {};//now[i]即为节点i所连的边的数量 Number of ways 

    当然,你也可以开动态数组。不过如果不是急需省内存,你就开吧。但是我没试过,也许会更繁琐。
    读入时也许读入重边,用指针判断判断就好了
    正常的BFS不用说了吧。。。
    判断一个节点是否在队列中,只需要一个数组标记就好了。

    具体代码

    #include <iostream>
    #include <string.h>
    #include <math.h>
    #include <limits.h>
    using namespace std;
    struct _Way//定义_Way类型,表示某点与y点相连,长度为len 
    {
        int y,len;  
    };
    int n,m;
    int q;
    _Way way[1001][1001] {};//way[i][j]标示i的第j条路 
    _Way* bz[1001][1001] {};//输入时用于判断,有时输入了重边,肯定要取最小的,这个标记了这个边的地址,方便判断 
    int now[1001] {};//now[i]即为节点i所连的边的数量 Number of ways 
    int dis[1001] {};//dis[i]即为从起点到i的最短距离 
    int min(int a,int b){return a<b?a:b;}//取最小值函数 
    void SPFA(int);
    int main()
    {
        ios::sync_with_stdio(false);
        cin>>n>>m;
        for(int i=1;i<=m;i++)
        {
            int x,y,len;
            cin>>x>>y>>len;
            if (bz[x][y]!=NULL)//判断这条边是否重了 
            {
                bz[x][y]->len=min(bz[x][y]->len,len);//如果重了边取最小值 
                continue;
            }
            now[x]++;
            way[x][now[x]].y=y;
            way[x][now[x]].len=len;
            bz[x][y]=&(way[x][now[x]]);//将它的地址赋给bz[x][y] 
        }
        cin>>q;
        SPFA(q);
        for(int i=1;i<=n;i++)
        {
            if (dis[i]==INT_MAX) cout<<"-1"<<endl;//如果到不了输出-1 
            else cout<<dis[i]<<endl;
        }
        return 0;
    }
    void SPFA(int q)//q表示起点 
    #define SIZE 2006
    {
        bool bz[1001] {};//bz[i]表示i节点是否在队列中 
        for(int i=1;i<=n;i++)
            dis[i]=INT_MAX;//初始化为无限大 
        int head=0,tail=1;
        int d[SIZE+1];
        dis[q]=0;//起点到起点,当然为0了 
        d[1]=q;
        bz[q]=1;
        do
        {
            head++;
            if (head>SIZE) head=1;//与下文一样,用循环队列 
            for(int i=1;i<=now[d[head]];i++)//将跟它相连的边都枚举一遍 
            {
                tail++;
                if (tail>SIZE) tail=1;
                d[tail]=way[d[head]][i].y;
                if (dis[d[tail]]<=dis[d[head]]+way[d[head]][i].len)//如果现在到那边不比之前的优,则删掉它 
                {
                    tail--;
                    if (tail<1) tail=SIZE;
                    continue;
                }
                dis[d[tail]]=dis[d[head]]+way[d[head]][i].len;//更新 
                if (bz[d[tail]])
                {
                    tail--;
                    if (tail<1) tail=SIZE;
                    continue;
                }
                bz[d[tail]]=1;//标记其以进入队列 
            }
            bz[d[head]]=0;//它出了队列,将之前的1改为0 
        }
        while (head!=tail);
    }

    优化

    C++者勿入

  • 相关阅读:
    5917
    安装wdcp后,反向代理全过程
    今天 想了个问题,阿里旺旺及时消息
    5917全部电影 我反代了一个站
    曾经4000多IP的站,被百度K了
    asp.net 出现Operation is not valid due to the current state of the object.
    自然语言处理 |文本相似度计算与文本匹配问题
    Node.js v16.13.0 连接MySQL数据库8.0.27失败问题
    NLP自然语言处理 | Prolog 语言入门教程:
    NLP自然语言处理 | TFIDF与余弦相似性的应用(二):找出相似文章
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145319.html
Copyright © 2011-2022 走看看