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:
SPFA算法学习笔记的更多相关文章
- Johnson算法学习笔记
\(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...
- Johnson 全源最短路径算法学习笔记
Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...
- C / C++算法学习笔记(8)-SHELL排序
原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...
- Manacher算法学习笔记 | LeetCode#5
Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...
- 某科学的PID算法学习笔记
最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...
- 算法学习笔记——sort 和 qsort 提供的快速排序
这里存放的是笔者在学习算法和数据结构时相关的学习笔记,记录了笔者通过网络和书籍资料中学习到的知识点和技巧,在供自己学习和反思的同时为有需要的人提供一定的思路和帮助. 从排序开始 基本的排序算法包括冒泡 ...
- BZOJ 2595: [Wc2008]游览计划 [DP 状压 斯坦纳树 spfa]【学习笔记】
传送门 题意:略 论文 <SPFA算法的优化及应用> http://www.cnblogs.com/lazycal/p/bzoj-2595.html 本题的核心就是求斯坦纳树: Stein ...
- R语言实现关联规则与推荐算法(学习笔记)
R语言实现关联规则 笔者前言:以前在网上遇到很多很好的关联规则的案例,最近看到一个更好的,于是便学习一下,写个学习笔记. 1 1 0 0 2 1 1 0 0 3 1 1 0 1 4 0 0 0 0 5 ...
- 二次剩余Cipolla算法学习笔记
对于同余式 \[x^2 \equiv n \pmod p\] 若对于给定的\(n, P\),存在\(x\)满足上面的式子,则乘\(n\)在模\(p\)意义下是二次剩余,否则为非二次剩余 我们需要计算的 ...
随机推荐
- 剑指offer——变态跳台阶
题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级.求该青蛙跳上一个n级的台阶总共有多少种跳法. 问题分析 由于每次跳的阶数不确定,没有一个固定的规律,但是可以了解的是后一次跳 ...
- Java虚拟机学习(1):体系结构 内存模型
一:Java技术体系模块图 Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代" ."非堆", 它用于存储虚拟机加载的类信息.常量.静态 ...
- 如何生成DLL文件
1.打开项目工程,点击Rebuild 2.Rebuild成功后,打开该项目所在文件目录 3.在路径里,在bin->Debug文件下可以看到刚生成成功的dll文件.
- HDU 4947 GCD Array 容斥原理+树状数组
GCD Array Time Limit: 11000/5500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total ...
- 关于SQLite数据库的作业
数据库的SQL预览代码我忘了复制了 只能截图 Students表: Course表: Score表: Teachcer表:
- php : mysql数据库操作类演示
设计目标: 1,该类一实例化,就可以自动连接上mysql数据库: 2,该类可以单独去设定要使用的连接编码(set names XXX) 3,该类可以单独去设定要使用的数据库(use XXX): 4,可 ...
- 《BI那点儿事》数据流转换——排序
排序转换允许对数据流中的数据按照某一列进行排序.这是五个常用的转换之一.连接数据源打开编辑界面,编辑这种任务.不想设置为排序列的字段不要选中,默认情况下所有列都会选中.如图所示,按照TotalSuga ...
- SQL*Loader实验笔记【二】
所有SQL*Loader实验笔记 实验案例总结(1-7): SQL*Loader实验笔记[一] 实验案例总结(8-13): SQL*Loader实验笔记[二] 实验案例总结(14-19 ...
- PHP的autoload机制的实现解析
在使用PHP的OO模式开发系统时,通常大家习惯上将每个类的实现都存放在一个单独的文件里,这样会很容易实现对类进行复用,同时将来维护时也很便利 一.autoload机制概述 在使用PHP的OO模式开发系 ...
- 【转】 memset()的效率以及源码分析
void *memset(void *s, int ch, size_t n);作用:将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值, 块的大小由第三个参数指定,这个函数通常 ...