SPFA算法学习笔记

一.理论准备

        为了学习网络流,先水一道spfa。

        SPFA算法是1994年西南交通大学段凡丁提出,只要最短路径存在,SPFA算法必定能求出最小值,SPFA对Bellman-Ford算法优化的关键之处在于意识到:只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。为什么队列为空就不改变了呢?就是因为要到下一点必须经过它的前一个邻接点。。SPFA可以处理负权边。很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。简洁起见,我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路。

        初始化: dis数组全部赋值为Inf(无穷大,不能是map[s][i]),path数组全部赋值为s(即源点),或者赋值为-1,表示还没有知道前驱,然后dis[s]=0;  表示源点不用求最短路径,或者说最短路就是0。将源点入队;另外记住在整个算法中有顶点入队了要记得标记vis数组,有顶点出队了记得消除那个标记(可能多次入队)。

        核心:读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队以此循环,直到队空为止就完成了单源最短路的求解。

        判断有无负环:如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图),假设这个节点的入度是k(无向权则就是这个节点的连接的边)如果进入这个队列超过k,说明必然有某个边重复了,即成环;换一种思路:用DFS,假设存在负环a1->a2->…->an->a1。那么当从a1深搜下去时又遇到了a1,那么直接可以判断负环了所有用。当某个节点n次进入队列,则存在负环,此时时间复杂度为O(n*m),n为节点,m为边。

        SPFA算法有两个优化算法 SLF 和 LLL: SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾。 LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出对进行松弛操作。 SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高约 50%。 在实际的应用中SPFA的算法时间效率不是很稳定,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法。个人觉得LLL优化每次要求平均值,不太好,为了简单,我们可以之间用c++STL里面的优先队列来进行SLF优化。

二.算法实现

        直接去把HDU1874AC了吧。

  1: import java.util.Comparator;
  2: import java.util.PriorityQueue;
  3: import java.util.Queue;
  4: import java.util.Scanner;
  5: /*
  6:  * 原来一直wa,重写了一遍,AC了
  7:  * SLF优化
  8:  */
  9: public class HD1874 {
 10:
 11:   static int n,m;
 12:   static int[][] map  = new int[205][205];
 13:   static int[] dis  = new int[205];
 14:   static boolean[] vis  = new boolean[205];
 15:   /*
 16:    * 路径最大值是10000,不能设置成10005就行,还要考虑和
 17:    * 也不能是整形最大值,否则一加就溢出了
 18:    */
 19:   static final int Inf = 0x3f3f3f3f;
 20:
 21:   public static void main(String[] args) {
 22:     Scanner sc = new Scanner(System.in);
 23:     // 记录前驱点 。若path[i]=j,表示从s到i的最短路径中i的前一个点是j
 24:     //int[] path;
 25:     int u,v,w;
 26:     while(sc.hasNext()) {
 27:       n = sc.nextInt();
 28:       m = sc.nextInt();
 29:       for(int i=0; i<205; i++) {
 30:         for(int j=i; j<205; j++) {
 31:           map[i][j] = Inf;
 32:           map[j][i] = Inf;
 33:         }
 34:       }
 35:       for(int i=0; i<m; i++) {
 36:         u = sc.nextInt();
 37:         v = sc.nextInt();
 38:         w = sc.nextInt();
 39:         //多重边
 40:         if(map[u][v]>w) {
 41:           map[v][u] = w;
 42:           map[u][v] = w;
 43:         }
 44:       }
 45:       int s = sc.nextInt();
 46:       int t = sc.nextInt();
 47:       spfa(s);
 48:       //题目上有st<n,所以不必判断dis[t]是否越界
 49:       //起点终点相同的话答案是0
 50:       if(Inf==dis[t]) {
 51:         System.out.println(-1);
 52:       }else {
 53:         System.out.println(dis[t]);
 54:       }
 55:     }
 56:   }
 57:
 58:   private static void spfa(int s) {
 59:
 60:     for(int i=0; i<205; i++) {
 61:       vis[i] = false;
 62:       //初始化为map[s][i]第一组数据就错了
 63:       dis[i] = Inf;
 64:     }
 65:     dis[s] = 0;
 66:     vis[s] = true;
 67:     Comparator<Integer> cmp = new Comparator<Integer>() {
 68:
 69:           public int compare(Integer o1, Integer o2) {
 70:             int i = (int)o1;
 71:             int j = (int)02;
 72:             if(dis[i]>dis[j]) {
 73:               return 1;
 74:             }else if(dis[i]==dis[j]){
 75:               return 0;
 76:             }else {
 77:               return -1;
 78:             }
 79:           }
 80:     };
 81:     //面向接口编程;205代表优先队列(是类)的容量
 82:     Queue<Integer> q = new PriorityQueue<Integer>(205, cmp);
 83:     q.clear();
 84:     q.offer(s);
 85:     while(!q.isEmpty()) {
 86:       int head = q.poll();
 87:       //该注意的是有些点可能重复入队,所以出队的点也要重新置未标记
 88:       vis[head] = false;
 89:       for(int i=0; i<n; i++) {
 90:         //dis[head]不可能是INF,map[head][i]可能是INF
 91:         int temp = dis[head] + map[head][i];
 92:         if(temp<dis[i]) {
 93:           //path[i] = head
 94:           dis[i] = temp;
 95:           if(!vis[i]) {
 96:             //用一个数组在此记录入队次数,大于n就存在负环;如何事先判断
 97:             q.offer(i);
 98:             vis[i] = true;
 99:           }
100:         }
101:       }
102:     }
103:   }
104:
105: }
106: 
时间: 2024-09-12 10:22:00

SPFA算法学习笔记的相关文章

Efficient Color Boundary Detection with Color-opponent Mechanisms算法学习笔记

这是一篇基于视觉颜色机制的边缘检测论文,原文分中文和英文版 中文版链接:中文版PDF 英文版链接:英文版PDF 项目主页:http://www.neuro.uestc.edu.cn/vccl/projcvpr2013.html 以下是我个人学习该算法后的理解,希望各位看官批评指正! 整个算法可分为以下几步: 1.输入一张彩色图像 2. 分别提取R-G-B三种颜色信息,并计算Y颜色信息,进行高斯滤波得到 3.设置连接权重ω ,通过式(1)得到R+wG和wR+G两种连接权值的的结果 $$Srg.Sg

Improved SLIC 算法学习笔记

之前有关于SLIC Superpixel算法的个人理解,这篇文章是对其改进算法Improved SLIC算法的理解. 改进点: sigma filter 用来避免错误分割: 聚类结束后,会基于颜色相似度将小聚类融入临近的聚类中. 改进聚类中心 原SLIC算法 使用平均值更新聚类中心 Improved SLIC算法 采用如下方法更新聚类中心,对应于改进点1: 其中δ_j表示,属于该聚类的所有像素点的亮度L的标准差,α是一个常数. 改进点2 原来的SLIC算法中,在进行迭代聚类后,得到一些小的聚类,

SLIC Superpixels 算法学习笔记

算法流程梳理如下: 原文下载:http://www.kev-smith.com/papers/SLIC_Superpixels.pdf 1.初始化: 通过对图像像素进行抽样,初始化k个聚类中心C_k ,步长为初始化聚类大小S,即将图像分为k个网格,取每个网格中心为初始聚类中心: 初始化每个像素点的标签lable为-1,每个像素点与聚类中心的距离distance为无穷大. 2.对初始化的聚类中心进行移动: 在该聚类中心相邻的8个像素点中,找到最小梯度方向,并将该点设为新的聚类中心,直至聚类中心不再

舍伍德(Sherwood)算法学习笔记

一.概念引入         设A是一个确定性算法,当它的输入实例为x时所需的计算时间记为tA(x).设Xn是算法A的输入规模为n的实例的全体,则当问题的输入规模为n时,算法A所需的平均时间为.这显然不能排除存在x∈Xn使得的可能性.希望获得一个随机化算法B,使得对问题的输入规模为n的每一个实例均有.这就是舍伍德算法设计的基本思想.当s(n)与tA(n)相比可忽略时,舍伍德算法可获得很好的平均性能.         概率算法的一个特点是对同一实例多次运用同一概率算法结果可能同.舍伍德算法(O(s

银行家算法学习笔记

一.概念引入         银行家算法( banker's algorithm )由 Dijkstra于1965提出,关键是将死锁的问题演示为一个银行家贷款的模型,由于能用于银行系统的现金贷款而出名.一个银行家向一群客户发放信用卡,每个客户有不同的信用额度.每个客户可以提出信用额度内的任意额度的请求,直到额度用完后再一次性还款.银行家承诺每个客户最终都能获得自己需要的额度.所谓"最终",是说银行家可以先挂起某个额度请求较大的客户的请求,优先满足小额度的请求,等小额度的请求还款后,再处

【学习】 R语言与机器学习学习笔记(1)K-近邻算法

前言 最近在学习数据挖掘,对数据挖掘中的算法比较感兴趣,打算整理分享一下学习情况,顺便利用R来实现一下数据挖掘算法. 数据挖掘里我打算整理的内容有:分类,聚类分析,关联分析,异常检测四大部分.其中分类算法主要介绍:K-近邻算法,决策树算法,朴素贝叶斯算法,支持向量机,神经网络,logistic回归. 写这份学习笔记主要以学校data mining课程的课件为主,会参考一堆的baidu,一堆的google,一堆的blog,一堆的book以及一堆乱七八糟的资料,由于精力有限,恕不能一一列出,如果有认

MySQL数据库学习笔记(一)

mysql|笔记|数据|数据库         我一直从事Informix和Oracle数据库开发,有一天发现网络上有一种小巧别致的数据库,被广泛使用,从MySQL的网站http://www.mysql.com/我下载了它的数据库软件,使用过后觉得真的挺好,这是我的一点学习笔记希望对各位初学者有点帮助. 1.       MySQL数据库介绍 MySQL 是瑞典的MySQL AB公司开发的一个可用于各种流行操作系统平台的关系数据库系统,它具有客户机/服务器体系结构的分布式数据库管理系统.MySQ

Hadoop学习笔记一 简要介绍

这里先大致介绍一下Hadoop. 本文大部分内容都是从官网Hadoop上来的.其中有一篇介绍HDFS的pdf文档,里面对Hadoop介绍的比较全面了.我的这一个系列的Hadoop学习笔记也是从这里一步一步进行下来的,同时又参考了网上的很多文章,对学习Hadoop中遇到的问题进行了归纳总结. 言归正传,先说一下Hadoop的来龙去脉.谈到Hadoop就不得不提到Lucene和Nutch.首先,Lucene并不是一个应用程序,而是提供了一个纯Java的高性能全文索引引擎工具包,它可以方便的嵌入到各种

作为一个新手的Oracle(DBA)学习笔记

Oracle数据库笔记 Jack Chaing 作者QQ595696297 交流群 127591054 祝大家学习进步. 如果大家想看Word版本的可以去下载:Word排版比较清晰一些. http://download.csdn.net/detail/jack__chiang/9810532 此笔记是作者本人去年开始从一个DBA新人的学习笔记,积累至今,希望拿出来给那些对DBA有兴趣的童孩学习,大家一起努力嘛. 此笔记记录了作者工作学习中从零基础的学习的记录,和从中遇见的问题与问题的解决!很高兴