(转)SPFA算法

原文地址:http://www.cnblogs.com/scau20110726/archive/2012/11/18/2776124.html

粗略讲讲SPFA算法的原理,SPFA算法是1994年西安交通大学段凡丁提出

是一种求单源最短路的算法

算法中需要用到的主要变量

int n;  //表示n个点,从1到n标号

int s,t;  //s为源点,t为终点

int d[N];  //d[i]表示源点s到点i的最短路

int p[N];  //记录路径(或者说记录前驱)

queue <int> q;  //一个队列,用STL实现,当然可有手打队列,无所谓

bool vis[N];   //vis[i]=1表示点i在队列中 vis[i]=0表示不在队列中

 

几乎所有的最短路算法其步骤都可以分为两步

1.初始化

2.松弛操作

 

初始化: d数组全部赋值为INF(无穷大);p数组全部赋值为s(即源点),或者赋值为-1,表示还没有知道前驱

             然后d[s]=0;  表示源点不用求最短路径,或者说最短路就是0。将源点入队;

    (另外记住在整个算法中有顶点入队了要记得标记vis数组,有顶点出队了记得消除那个标记)

队列+松弛操作

读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队

以此循环,直到队空为止就完成了单源最短路的求解

 

SPFA可以处理负权边

定理: 只要最短路径存在,上述SPFA算法必定能求出最小值。

证明:

  每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。(证毕)

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

判断有无负环:

  如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

 

 

 

SPFA的两种写法,bfs和dfs,bfs判别负环不稳定,相当于限深度搜索,但是设置得好的话还是没问题的,dfs的话判断负环很快

int spfa_bfs(int s)
{
    queue <int> q;
    memset(d,0x3f,sizeof(d));
    d[s]=0;
    memset(c,0,sizeof(c));
    memset(vis,0,sizeof(vis));

    q.push(s);  vis[s]=1; c[s]=1;
    //顶点入队vis要做标记,另外要统计顶点的入队次数
    int OK=1;
    while(!q.empty())
    {
        int x;
        x=q.front(); q.pop();  vis[x]=0;
        //队头元素出队,并且消除标记
        for(int k=f[x]; k!=0; k=nnext[k]) //遍历顶点x的邻接表
        {
            int y=v[k];
            if( d[x]+w[k] < d[y])
            {
                d[y]=d[x]+w[k];  //松弛
                if(!vis[y])  //顶点y不在队内
                {
                    vis[y]=1;    //标记
                    c[y]++;      //统计次数
                    q.push(y);   //入队
                    if(c[y]>NN)  //超过入队次数上限,说明有负环
                        return OK=0;
                }
            }
        }
    }

    return OK;

}

int spfa_dfs(int u)
{
    vis[u]=1;
    for(int k=f[u]; k!=0; k=e[k].next)
    {
        int v=e[k].v,w=e[k].w;
        if( d[u]+w < d[v] )
        {
            d[v]=d[u]+w;
            if(!vis[v])
            {
                if(spfa_dfs(v))
                    return 1;
            }
            else
                return 1;
        }
    }
    vis[u]=0;
    return 0;
}

时间: 2024-10-14 12:08:57

(转)SPFA算法的相关文章

POJ 3259 Wormholes SPFA算法题解

本题其实也可以使用SPFA算法来求解的,不过就一个关键点,就是当某个顶点入列的次数超过所有顶点的总数的时候,就可以判断是有负环出现了. SPFA原来也是可以处理负环的. 不过SPFA这种处理负环的方法自然比一般的Bellman Ford算法要慢点了. #include <stdio.h> #include <string.h> #include <limits.h> const int MAX_N = 501; const int MAX_M = 2501; const

POJ 1847 Tram 【最短路,spfa算法,题意理解是关键呀!!】

Tram Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 13468   Accepted: 4954 Description Tram network in Zagreb consists of a number of intersections and rails connecting some of them. In every intersection there is a switch pointing to t

[知识点]SPFA算法

// 此博文为迁移而来,写于2015年4月9日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vx93.html 1.前言 最短路算法有很多种,类似于Floyd和Dijkstra都是很早之前就学了的.其实每种最短路算法有各自的优势.Floyd适合于跑完全图,但是效率太慢(O(n3)).Dijkstra适合于跑没有负权的图,效率为O(n2).而今天介绍的SPFA算法,是有一位中国人——段凡丁所提出来的(其实我很想吐个槽.

spfa算法详解

program: #include<cstdio> using namespace std; struct node {int x; int value; int next; }; node e[60000]; int visited[1505],dis[1505],st[1505],queue[1000]; int main() { int n,m,u,v,w,start,h,r,cur; freopen("c.in","r",stdin); freo

HDU 2112 HDU Today【最短路+map容器,spfa算法+Dijkstra算法】

HDU Today Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 25102    Accepted Submission(s): 6067 Problem Description 经过锦囊相助,海东集团终于度过了危机,从此,HDU的发展就一直顺风顺水,到了2050年,集团已经相当规模了,据说进入了钱江肉丝经济开发区500强.这时候

hihocoder1093最短路(SPFA算法)

算法描述: 首先将起始节点入队,然后每次从队列中取出一个节点,更新其邻接点的最短路径值,若有被更新,则检查该邻接点是否在队列中,若不在队列中,则入队.如此循环直到队空时算法结束. 当图中不存在负环时,算法一定会收敛,并能得到所求最短路.原因在于,每次从队列中取出一个节点并更新其邻接点的过程其实都是在向最优解逼近的过程,且只有当有更新的时候才可能向队列增加节点.由于不断的再向最优解逼近,所以到最后一定会达到最优解且不再有节点入队,队列最终为空,算法收敛.SPFA算法与dijkstra相比有一个好处

SPFA算法

适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了. 我们约定有向加权图G不存在负权回路,即最短路径一定存在.当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点. 算法思想:我们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G.我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计

Cocos2d-x 地图行走的实现2:SPFA算法

上一节<Cocos2d-x 地图行走的实现1:图论与Dijkstra算法> http://blog.csdn.net/stevenkylelee/article/details/38408253 本节实践另一种求最短路径算法:SPFA 1.寻路算法实现上的优化 上一节我们实现的Dijkstra用了一个哈希表来保存搜索到的路径树.如果能用直接的访问的方式,就不要用哈希表,因为直接访问的方式会比哈希表更快.我们修改一下图顶点的数据结构.如下: /* 图顶点 */ class Vertex { fr

最短路 spfa算法

问题描述 给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环).请你计算从1号点到其他点的最短路(顶点从1到n编号). 输入格式 第一行两个整数n, m. 接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边. 输出格式 共n-1行,第i行表示1号点到i+1号点的最短路. 样例输入 3 31 2 -12 3 -13 1 2 样例输出 -1-2 数据规模与约定 对于10%的数据,n = 2,m = 2. 对于30%的数据,n <= 5,m <= 10. 对

(最短路径算法整理)dijkstra、floyd、bellman-ford、spfa算法模板的整理与介绍

这一篇博客以一些OJ上的题目为载体.整理一下最短路径算法.会陆续的更新... 一.多源最短路算法--floyd算法 floyd算法主要用于求随意两点间的最短路径.也成最短最短路径问题. 核心代码: /** *floyd算法 */ void floyd() { int i, j, k; for (k = 1; k <= n; ++k) {//遍历全部的中间点 for (i = 1; i <= n; ++i) {//遍历全部的起点 for (j = 1; j <= n; ++j) {//遍历