【C++学习笔记】强大的算法——spfa
- spfa的定义
PFA算法的全称是:Shortest Path Faster Algorithm,用于求单源最短路,由西南交通大学段凡丁于1994年发表。当给定的图存在负边时,Dijkstra算法就无能为力了,然而bellman_ford算法的复杂度又太高。在这种情况下spfa算法就有了用武之地。
- spfa实现
为了简单起见,我们首先约定有向图G中不存在负权回路(如果有就不会存在最短路了),在执行spfa时如果一个节点入队次数超过 n 次(n为节点总数),那么图中就存在负环。我们用数组 dis[ ] 来储存最短路的当前最优值,用邻接表(推荐)或邻接矩阵来储存图。
- 首先设立一个先进先出的队列 q 来保存待优化的节点,再优化时依次取出队首的节点U,并利用节点U当前的最短路径最优值(dis[u] )对离开U所指向的节点V施行 松弛操作 。如果V的最短路径估计值有所调整(),且V不在当前的队列 q 中,那么就将 V 入队。不断重复上述操作直到队列为空。
- 关于松弛操作: 松弛操作的原理是定理: “三角形两边之和大于第三边” ,在信息学中我们叫它三角不等式。所谓对结点 i , j进行松弛,就是判定是否dis[ j ] > dis[ i ] + w[ i , j ],如果该式成立则将 dis[ j ] 减小到 dis[ i ] + w[ i , j ],否则不动。
利用下图来演示一下spfa算法的过程:


- spfa和bfs的区别:
SPFA 在形式上和广度(宽度)优先搜索非常类似,不同的是bfs中一个点出了队列就不可能重新进入队列,但是SPFA中一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本身被改进(重新入队),于是再次用来改进其它的点,这样反复迭代下去。
- 具体代码实现
其中 vis[ i ] 判断点 i 是否已经入队,a[ i ][ n ] 表示从 i 到 n 的权值
void spfa ( int s ) { //求s到其它各点的最短路
for ( int i = ; i <= n; i ++ ) {
dis[i] = ; //初始化
}
dis[ s ] = ; vis[s] = ; q[ ] = s; //以 S 为起点
int i, v, head = , tail = ;
while ( head < tail ) { //判断队列非空
head ++;
v = q[ head ];
vis[ v ] = ; //释放队首元素,因为该节点可能下次用来松弛
for ( int i = ; i <= n; i ++ ) {
if ( a[ v ][ i ] > && dis[ i ] > dis[ v ] + a[ v ][ i ] ) {
dis[ i ] = dis[ v ] + a[ v ] [ i ]; //符合条件,修改最短路
if ( vis[ i ] == ) { //如果扩展节点不在队列中,入队!
tail ++;
q[ tail ] = i;
vis[ i ] = ;
}
}
}
}
}
- spfa的优化
- dfs
在以上的 spfa算法 中,当图中存在负环时,算法的复杂度将会退化成 O(m * n)。因此我们试着用 bfs 来改进一下。核心思路是 每当更新一个节点 u 时,从该节点开始递归进行下一次迭代。相比于原来的算法,使用 bfs 有一个天然的优势,在环上走了一圈后如果回到已经遍历过的节点,则说明图中存在负环(比原先的方法快了不止一点)。用二维数组 a[ ][ ] 和 v[ ][ ] 来存图,其中 a[ i ][ 0 ] 储存从节点 i 出发共有几条边, a[ i ][ n ] 储存从节点 i 出发的第 n 条边的终点,v[ n ][ m ] 储存边从 n 到 m 的权值。(由此可见好的数据结构可以为我们省下大量的时间)
void spfa_dfs ( int st ) { //寻找以 节点st 为原点的最短路
for ( int i = ; i <= b[st][]; i ++ ) { //遍历节点所连接的边
if ( dis[ a[st][i] ] > dis[st] + v[st][ a[st][i] ] ) {
dis[ a[st][i] ] = dis[s] + v[st][ a[st][i] ]; //标准的spfa
spfa ( b[st][i] ); //从入队的节点开始下一次迭代
}
}
} /* 用数组 dis[ ] 来储存结果*/
- 前向星优化
前向星型表示法(star)的思想和邻接表表示法的的思想有一定的相似之处,对于每个节点它也是记录从该节点出发的所有的边,但它没有采用单向链表而是用一个一维数组表示。也就是说,在该数组中首先存放从结点1出发的所有弧,然后接着存放从节点2出发的所有孤,依此类推,最后存放从结点n出发的所有孤。对每条弧,要依次存放其起点、终点、权的数值等有关信息。这实际上相当于对所有弧给出了一个顺序和编号,只是从同一结点出发的弧的顺序可以任意排列。此外,为了能够快速检索从每个节点出发的所有弧,我们一般还用一个数组记录每个结点出发的弧的起始地址(即弧的编号)。在这种表示法中,可以快速检索从每个结点出发的所有弧,这种星形表示法称为前向星形(forward star)表示法,也是一种常见的存图方法。
具体代码实现
int first[]; /* 关于 前向星 详见我的另一篇随笔 */
struct edge {
int point, next, len;
};
edge e[] void add(int i, int u, int v, int w){
e[i].point = v;
e[i].next = first[u];
e[i].len = w;
first[u] = i;
} int main(){
int n, m;
cin >> n >> m;
for ( int i = ; i <= m; i ++ ) {
int u, v, w;
cin >> u >> v >> w;
add ( i, u, v, w );
}
for ( int i = ; i <= n; i ++ ) {
cout << "from " << i << endl;
for ( int j = first[i]; j ; j = e[j].next ) //这就是遍历边了
cout << "to " << e[j].point << " length= " << e[j].len << endl;
}
}
贴个友链~ https://www.cnblogs.com/dreagonm/
【C++学习笔记】强大的算法——spfa的更多相关文章
- GMM高斯混合模型学习笔记(EM算法求解)
提出混合模型主要是为了能更好地近似一些较复杂的样本分布,通过不断添加component个数,能够随意地逼近不论什么连续的概率分布.所以我们觉得不论什么样本分布都能够用混合模型来建模.由于高斯函数具有一 ...
- 强化学习-学习笔记7 | Sarsa算法原理与推导
Sarsa算法 是 TD算法的一种,之前没有严谨推导过 TD 算法,这一篇就来从数学的角度推导一下 Sarsa 算法.注意,这部分属于 TD算法的延申. 7. Sarsa算法 7.1 推导 TD ta ...
- 【学习笔记】 Adaboost算法
前言 之前的学习中也有好几次尝试过学习该算法,但是都无功而返,不仅仅是因为该算法各大博主.大牛的描述都比较晦涩难懂,同时我自己学习过程中也心浮气躁,不能专心. 现如今决定一口气肝到底,这样我明天就可以 ...
- 挑子学习笔记:DBSCAN算法的python实现
转载请标明出处:https://www.cnblogs.com/tiaozistudy/p/dbscan_algorithm.html DBSCAN(Density-Based Spatial Clu ...
- 普通平衡树学习笔记之Splay算法
前言 今天不容易有一天的自由学习时间,当然要用来"学习".在此记录一下今天学到的最基础的平衡树. 定义 平衡树是二叉搜索树和堆合并构成的数据结构,它是一 棵空树或它的左右两个子树的 ...
- 【算法学习笔记】Meissel-Lehmer 算法 (亚线性时间找出素数个数)
「Meissel-Lehmer 算法」是一种能在亚线性时间复杂度内求出 \(1\sim n\) 内质数个数的一种算法. 在看素数相关论文时发现了这个算法,论文链接:Here. 算法的细节来自 OI w ...
- python学习笔记(MD5算法)
博主最近进度停滞了 对web开发理解欠缺好多内容 今天整理下MD5算法,这个涉及到mysql数据库存储用户表密码字段的时候 一般是带有加密的 # -*- coding: utf-8 -*- impor ...
- 【学习笔记】分类算法-k近邻算法
k-近邻算法采用测量不同特征值之间的距离来进行分类. 优点:精度高.对异常值不敏感.无数据输入假定 缺点:计算复杂度高.空间复杂度高 使用数据范围:数值型和标称型 用例子来理解k-近邻算法 电影可以按 ...
- Web安全学习笔记之DES算法实例详解
转自http://www.hankcs.com/security/des-algorithm-illustrated.html 译自J. Orlin Grabbe的名作<DES Algorith ...
随机推荐
- (反NIM)
题目大意是和普通的NIM游戏一样,但是却是取到最后一个是输的,天真的以为就是反过来,其实并不是这样的 结论 先手必胜的条件为 ①:所有堆的石子数均=1,且有偶数堆. ②:至少有一个堆的石子数>1 ...
- 洛谷P3195||bzoj1010 [HNOI2008]玩具装箱TOY
洛谷P3195 bzoj1010 设s数组为C的前缀和 首先$ans_i=min_{j<i}\{ans_j+(i-j-1+s_i-s_j-L)^2\}$ (斜率优化dp)参考(复读)https: ...
- Python 列表list 和 字符串str 互转
一.列表list转字符串str 命令(python2.x):''.join(list) 命令(python2.x):''.join(str(s) for s in list) 其中,引号中是字符之间的 ...
- python入门之time模块和datetime模块
time模块 时间三种表示:时间戳(秒单位),struct_time(元组,可以分开调用),指定格式(格式化) time.sleep() 等待5秒钟 time.time() 返回时间戳 time.ct ...
- Unity GL 画圆
Unity下GL没有画圆的函数,只能自己来了. 如果能帮到大家,我也很高兴. 虽然没有画圆的函数,但是能画直线,利用这一点,配合微积分什么的,就可以画出来了.反正就是花很多连在一起的直线,每条直线足够 ...
- Android入门:封装一个HTTP请求的辅助类
前面的文章中,我们曾经实现了一个HTTP的GET 和 POST 请求: 此处我封装了一个HTTP的get和post的辅助类,能够更好的使用: 类名:HttpRequestUtil 提供了如下功能: ( ...
- 我对USB的认识
一.USB协议规范 (1) 基本概念 每一个设备(device)会有一个或者多个的逻辑连接点在里面,每个连接点叫endpoint.每个endpoint有四种数据传送方式:控制(Contr ...
- MATLAB之折线图、柱状图、饼图以及常用绘图技巧
MATLAB之折线图.柱状图.饼图以及常用绘图技巧 一.折线图 参考代码: %图1:各模式直接成本预测 %table0-table1为1*9的数组,记录关键数据 table0 = data_modol ...
- Java开发工具IntelliJ IDEA创建Andriod项目示例说明
IntelliJ IDEA社区版作为一个轻量级的Java开发IDE,是一个开箱即用的Android开发工具. 注意:在本次的教程中我们将以Android平台2.2为例进行IntelliJ IDEA的使 ...
- 【Java/Android性能优 6】Android 图片SD卡缓存 使用简单 支持预取 支持多种缓存算法 支持不同网络类型 支持序列化
本文转自:http://www.trinea.cn/android/android-imagesdcardcache/ 本文主要介绍一个支持图片自动预取.支持多种缓存算法.支持数据保存和恢复的图片Sd ...