一.理论准备

为了学习网络流,先水一道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: 

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

  1. Johnson算法学习笔记

    \(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...

  2. Johnson 全源最短路径算法学习笔记

    Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...

  3. C / C++算法学习笔记(8)-SHELL排序

    原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...

  4. Manacher算法学习笔记 | LeetCode#5

    Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...

  5. 某科学的PID算法学习笔记

    最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...

  6. 算法学习笔记——sort 和 qsort 提供的快速排序

    这里存放的是笔者在学习算法和数据结构时相关的学习笔记,记录了笔者通过网络和书籍资料中学习到的知识点和技巧,在供自己学习和反思的同时为有需要的人提供一定的思路和帮助. 从排序开始 基本的排序算法包括冒泡 ...

  7. BZOJ 2595: [Wc2008]游览计划 [DP 状压 斯坦纳树 spfa]【学习笔记】

    传送门 题意:略 论文 <SPFA算法的优化及应用> http://www.cnblogs.com/lazycal/p/bzoj-2595.html 本题的核心就是求斯坦纳树: Stein ...

  8. R语言实现关联规则与推荐算法(学习笔记)

    R语言实现关联规则 笔者前言:以前在网上遇到很多很好的关联规则的案例,最近看到一个更好的,于是便学习一下,写个学习笔记. 1 1 0 0 2 1 1 0 0 3 1 1 0 1 4 0 0 0 0 5 ...

  9. 二次剩余Cipolla算法学习笔记

    对于同余式 \[x^2 \equiv n \pmod p\] 若对于给定的\(n, P\),存在\(x\)满足上面的式子,则乘\(n\)在模\(p\)意义下是二次剩余,否则为非二次剩余 我们需要计算的 ...

随机推荐

  1. 161215、MySQL 查看表结构简单命令

    一.简单描述表结构,字段类型desc tabl_name;显示表结构,字段类型,主键,是否为空等属性,但不显示外键.二.查询表中列的注释信息select * from information_sche ...

  2. OBD K线抓包 III

    14230 HL激活, 5BPS又称 00  //电平激活 C1 33 F1 81 66  //14230的Enter命令 83 F1 11 C1 EF 8F C4 //回应了,一个命令就回应了... ...

  3. [已解决] 点击 【Show in system explorer】Eclipse卡死,未响应

    新版的Eclipse自带了 [Show in system explorer] 功能很方便,有一天突然不好用了,点它Eclipse就卡死, 可能由以下原因导致的: (可能性最大)windows本身有问 ...

  4. (原创) cocos2dx使用Curl连接网络(客户端)

    0. 环境: winxpsp3, vs2010, cocos2dx@2.1.4 1. 新建一个Helloworld工程 2. HelloworldScene.h里面重写virtual bool ccT ...

  5. mac攻略(一) -- git使用

    1.首先在官网下载 https://git-scm.com/download/mac   2.然后安装git(mac自带git)   3设置Git的user name和email:   $ git c ...

  6. find函数

    Python find() 方法检测字符串中是否包含子字符串 str ,如果指定 beg(开始) 和 end(结束) 范围,则检查是否包含在指定范围内,如果包含子字符串返回开始的索引值,否则返回-1. ...

  7. [问题2014A11] 解答

    [问题2014A11]  解答 我们需要利用以下关于幂等阵判定的结论,它是复旦高代书第 142 页的例 3.6.4: 结论  设 \(A\) 为 \(n\) 阶方阵, 则 \(A^2=A\) 当且仅当 ...

  8. iOS 推送

    1,推送已至,点通知栏: a)若App在UIApplicationStateActive(活动)状态,调用 //接收到推送消息 - (void)application:(UIApplication * ...

  9. CSS3的chapter4

    段落样式: 行高——line-height p { line-height:25px | 150% | normal;} 段落缩进——text-indent p { text-indent:2em;} ...

  10. php中的访问修饰符 private、protected、public的作用范围

    1. private 只能在类内部使用 2. protected 可以在类内部和继承类里使用.类外部不能使用[即实例化后的对象无法调用] 3. public 全部范围适用. 4.子类复写父类中的方法时 ...