zoukankan      html  css  js  c++  java
  • 最短路--SPFA

    SPFA 算法

    算法优点

            1.时间复杂度比普通的Dijkstra和Ford

            2.能够计算负权图问题。

            3.能够判断是否有负环 (即:每跑一圈,路径会减小,所以会一直循环跑下去)。

    期望的时间复杂度O(k*e), 其中k为所有顶点进队的平均次数,e是边的数量,可以证明k一般小于等于2。

    实现方法:

      1.存入图。可以使用链式前向星或者vocter

            2.开一个队列,先将开始的节点放入。

            3.每次从队列中取出一个节点X,遍历与X相通的Y节点,查询比对  Y的长度X的长度+ X与Y的长度

                如果X的长度+ X与Y的长度 Y的长度,说明需要更新操作。

                        1).存入最短路。

                        2).由于改变了原有的长度,所以需要往后更新,与这个节点相连的最短路。(即:判断下是否在队列,在就不用重复,不在就加入队列,等待更新)。

                        3).在这期间可以记录这个节点的进队次数,判断是否存在负环。

            4.直到队空。

    判断有无负环:如果某个点进入队列的次数超过N次则存在负环

     

    模拟过程:

    首先建立起始点a到其余各点的最短路径表格

                                      

    首先源点a入队,当队列非空时:

            1、队首元素(a)出队,对以a为起始点的所有边的终点依次进行松弛操作(此处有b,c,d三个点),此时路径表格状态为:

                                      

    在松弛时三个点的最短路径估值变小了,而这些点队列中都没有出现,这些点需要入队,此时,队列中新入队了三个结点b,c,d

    队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e点),此时路径表格状态为:

                                     

    在最短路径表中,e的最短路径估值也变小了,e在队列中不存在,因此e也要入队,此时队列中的元素为c,d,e

    队首元素c点出队,对以c为起始点的所有边的终点依次进行松弛操作(此处有e,f两个点),此时路径表格状态为:

                                     

    在最短路径表中,e,f的最短路径估值变小了,e在队列中存在,f不存在。因此e不用入队了,f要入队,此时队列中的元素为d,e,f

     队首元素d点出队,对以d为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

                                   

    在最短路径表中,g的最短路径估值没有变小(松弛不成功),没有新结点入队,队列中元素为f,g

    队首元素f点出队,对以f为起始点的所有边的终点依次进行松弛操作(此处有d,e,g三个点),此时路径表格状态为:

                                   

    在最短路径表中,e,g的最短路径估值又变小,队列中无e点,e入队,队列中存在g这个点,g不用入队,此时队列中元素为g,e

    队首元素g点出队,对以g为起始点的所有边的终点依次进行松弛操作(此处只有b点),此时路径表格状态为:

                               

    在最短路径表中,b的最短路径估值又变小,队列中无b点,b入队,此时队列中元素为e,b队首元素e点出队,对以e为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

                              

    在最短路径表中,g的最短路径估值没变化(松弛不成功),此时队列中元素为b

    队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e这个点),此时路径表格状态为:

                             

    在最短路径表中,e的最短路径估值没变化(松弛不成功),此时队列为空了

    最终a到g的最短路径为14

    以上转载自:http://keyblog.cn/article-21.html

    代码

    int way[250][250],dis[250],vis[250],cnt[250];
    //way记录路径关系,dis[i]记录起点到点j的最近距离,vis[i]标记点是否在队列中,cnt[i]记录点i进入队列的次数
    int n,m;
    void init()
    {
        for(int i=0;i<n;i++)//先初始化way
        {
            for(int j=0;j<n;j++)
            {
                if(i==j)
                    way[i][j]=0;
                else
                    way[i][j]=mx;
            }
        }
    }
    
    void spfa(int st)//st是起点
    {
        for(int i=0;i<n;i++)//这里点编号是从0开始的
            dis[i]=mx;
        memset(vis,0,sizeof(vis));
        memset(cnt,0,sizeof(cnt));
        vis[st]=1;
        cnt[st]=1;
        dis[st]=0;
        queue<int>p;
        p.push(st);
        while(!p.empty())
        {
            int now=p.front();
            p.pop();
            vis[now]=0;
            for(int i=0;i<n;i++)
            {
                if(dis[now]+way[now][i]<dis[i])
                {
                    dis[i]=dis[now]+way[now][i];
                    if(vis[i]==0)//如果点不在队列里面
                    {
                        p.push(i);
                        vis[i]=1;
                        cnt[i]++;
                        if(cnt[i]>n)//如果这个点加入超过n次,说明存在负圈,直接返回 
                            return ;
                    }
                }
            }
        }
    
    }

    模板题:https://www.cnblogs.com/-citywall123/p/11324215.html

  • 相关阅读:
    bzoj4010 [HNOI2015]菜肴制作
    PHP--------TP中的ajax请求
    二维数组去重
    手机号138-0013-8000格式存储
    spring4-2-bean配置-1-依赖注入
    spring4-1-Spring的简单介绍
    Result Grouping / Field Collapsing-结果分组
    vim自动补全
    vim配置-程序员【转】
    服务端程序设计和实现总结 【转】
  • 原文地址:https://www.cnblogs.com/-citywall123/p/11324246.html
Copyright © 2011-2022 走看看