文章目录

  • 1 基本介绍
  • 2 基础SPFA算法
  • 3 SLF优化
  • 4 LLL优化
  • 5 SLF+LLL优化
  • 6 DFS优化

1 基本介绍

若读者还不太了解SPFA算法,指路一篇博客:https://blog.csdn.net/hzf0701/article/details/107691013
关于SPFA算法,它已经死了,很多出题人都会故意卡SPFA算法,所以我们若进行优化,说不定就能过呢。但u1s1,对于正权图来说,还是老老实实用Dijkstra算法吧,贼香!

进入正题:我们以vector模拟邻接表存储为例。(哪种都一样,只是存储方式不同而已,主要是算法中心思想)

先介绍基本结构信息:

const int maxn=105;//顶点最大数
const int M=1e4;//边最大数
const int inf=0x3f3f3f3f;
int S,E;//起点与终点。
int n,m;//顶点数与边数。
int cnt[maxn];//cnt[i]记录的是i顶点的入队次数。若大于n,则必然存在负环。
typedef struct Edge{int to;//边的终端节点int w;//边权值。Edge(int to,int w):to(to),w(w){}//构造函数
}Edge;
vector<Edge> graph[maxn];//用vector模拟邻接表
int dis[maxn];//存储所有顶点到源点的距离。
bool visited[maxn];//判断是否在队列中。
void init(){//初始化函数。memset(cnt,0,sizeof(cnt));memset(dis,inf,sizeof(dis));memset(visited,false,sizeof(visited));
}

2 基础SPFA算法

int spfa(int S){queue<int> q;dis[S]=0;int temp;q.push(S);cnt[S]++;visited[S]=true; //入队即true。int flag=0;         //标志,若为真,则表示存在负环。while(!q.empty()){temp=q.front();q.pop();visited[temp]=false; //出队则false。int v,w;int t=graph[temp].size();//避免多次调用此函数。//松弛操作for(int i=0;i<t;i++){v=graph[temp][i].to;w=graph[temp][i].w;if(dis[v]>dis[temp]+w){dis[v]=dis[temp]+w;//更新最短路径if(!visited[v]){//判断是否在队列中q.push(v);cnt[v]++;if(cnt[v]>n){flag=1;return flag;}visited[v]=true;}}}}return flag;
}

3 SLF优化

SLF优化,即Small Label First策略,使用STL中的双端队列deque容器来实现,较为常用。

这个顾名思义就是在原有的SPFA算法中每次出队进行判断扩展出的点与队头元素进行判断,若小于进队头,否则入队尾。即:对要加入队列的点 u,如果 dist[u] 小于队头元素 v 的 dist[v],将其插入到队头,否则插入到队尾

注:队列为空时直接插入队尾。

int spfa_slf(int S){deque<int> q;dis[S]=0;int temp;q.push_back(S);cnt[S]++;visited[S]=true; //入队即true。int flag=0;         //标志,若为真,则表示存在负环。while(!q.empty()){temp=q.front();q.pop_front();visited[temp]=false; //出队则false。int v,w;int t=graph[temp].size();//避免多次调用此函数。//松弛操作for(int i=0;i<t;i++){v=graph[temp][i].to;w=graph[temp][i].w;if(dis[v]>dis[temp]+w){dis[v]=dis[temp]+w;//更新最短路径if(!visited[v]){//判断是否在队列中if(!q.empty()&&dis[v]>dis[q.front()]){//和队头元素进行比较。q.push_back(v);}else q.push_front(v);cnt[v]++;if(cnt[v]>n){flag=1;return flag;}visited[v]=true;}}}}return flag;
}

4 LLL优化

LLL优化即:Large Label Last策略。顾名思义就是大的放后面,这个不是针对要入队的元素,而是针对要出队的元素,我们是这样子来处理的:设队首元素为 temp ,每次松弛时进行判断,队列中所有 dis 值的和为sum,队列元素为num 。

若 dist[ temp] *num>sum ,则将 temp 取出插入到队尾,查找下一元素,直到找到某一个 temp 使得 dis[ temp ]*sum <= x ,则将 temp出队进行松弛操作。

int spfa_lll(int S){queue<int> q;int sum,num;//队列中dis总和和顶点数dis[S]=0;int temp;q.push(S);sum=dis[S],num=1;cnt[S]++;visited[S]=true; //入队即true。int flag=0;         //标志,若为真,则表示存在负环。while(!q.empty()){temp=q.front();while(dis[temp]*num>sum){//LLL优化关键在这。q.pop();q.push(temp);temp=q.front();}q.pop();//更新队列中dis总和和顶点数目。num--;sum-=dis[temp];visited[temp]=false; //出队则false。int v,w;int t=graph[temp].size();//避免多次调用此函数。//松弛操作for(int i=0;i<t;i++){v=graph[temp][i].to;w=graph[temp][i].w;if(dis[v]>dis[temp]+w){dis[v]=dis[temp]+w;//更新最短路径if(!visited[v]){//判断是否在队列中q.push(v);//更新队列中dis总和和顶点数目。sum+=dis[v];num++;cnt[v]++;if(cnt[v]>n){flag=1;return flag;}visited[v]=true;}}}}return flag;
}

5 SLF+LLL优化

我们根据前面得知SLF是对入队顶点进行判断,LLL是对出队顶点判断的,两者互不影响,那么不就可以结合了吗?

int spfa_slf_lll(int S){deque<int> q;dis[S]=0;int temp;q.push_back(S);int sum=dis[S];int num=1;cnt[S]++;visited[S]=true; //入队即true。int flag=0;         //标志,若为真,则表示存在负环。while(!q.empty()){temp=q.front();while(dis[temp]*num>sum){//lll优化q.pop_front();q.push_back(temp);temp=q.front();}q.pop_front();sum-=dis[temp];num--;visited[temp]=false; //出队则false。int v,w;int t=graph[temp].size();//避免多次调用此函数。//松弛操作for(int i=0;i<t;i++){v=graph[temp][i].to;w=graph[temp][i].w;if(dis[v]>dis[temp]+w){dis[v]=dis[temp]+w;//更新最短路径if(!visited[v]){//判断是否在队列中if(!q.empty()&&dis[v]>dis[q.front()]){//和队头元素进行比较。q.push_back(v);//大于入队尾}else q.push_front(v); //小于入队头sum+=dis[v];num++;cnt[v]++;if(cnt[v]>n){flag=1;return flag;}visited[v]=true;}}}}return flag;
}

6 DFS优化

我们知道SPFA算法是利用BFS的思想的,由于采用广度优先的思想,每当我们扩展出一个新的节点,总是把它放到队列的末尾,其缺点是中断了迭代的连续性。而实际上如果采用深度优先的思想,我们可以直接从这个新节点继续往下扩展,则我们就可以不断递归进行求解。我们用DFS的思想来代替BFS,这就是DFS优化。可这样优化真的好吗?这种优化常常用于判断正/负环,时间复杂度可以达到O(m)(m是边)。思路是,我们每一次dfs的时候如果走回之前dfs过的点,那就是有环,除了这个dfs的标记,我们还可以打另一个vis数组记录更新过权值的节点,以后就不必重复更新,大大降低复杂度。如果只是要求最短路径,还是使用前三种优化吧。用DFS优化我们要使用一个访问数组来判断一个点是否再次走过,不同于上面的visited。

我们先要进行初始化,注意:绝对不能放在spfa_dfs函数中。

bool visited[maxn];//判断是否访问过。
dis[S]=0;
memset(visited,false,sizeof(visited));
int flag=0;//标志位,判断是否存在负环。

void spfa_dfs(int temp){int v,w;int t=graph[temp].size();for(int i=0;i<t;i++){v=graph[temp][i].to;w=graph[temp][i].w;if(dis[v]>dis[temp]+w){if(visited[v]){//判断是否在一条路径上出现了多次。flag=true;return;}else{dis[v]=dis[temp]+w;visited[v]=true;spfa_dfs(v);//顺着这条路继续扩展。}}}
}

以上为博主自己整理,自己测试都没有问题的。若有任何疑问都可以私信我或者在评论区留言,我都会给予回复,愿能帮助到你。

SPFA算法的四种优化(SLF,LLL,SLF+LLL,DFS)相关推荐

  1. pytorch梯度下降函数_Pytorch中常用的四种优化器SGD、Momentum、RMSProp、Adam

    来源:AINLPer微信公众号 编辑: ShuYini 校稿: ShuYini 时间: 2019-8-16 引言     很多人在使用pytorch的时候都会遇到优化器选择的问题,今天就给大家介绍对比 ...

  2. 三种快排及四种优化方式

    本文是转载文章,文章的来源:csdn博客 博主:silentsharer 文章: 三种快排及四种优化方式 博文地址:https://blog.csdn.net/hacker00011000/artic ...

  3. ListView的四种优化方式

    ListView的四种优化方式 优化方式一: convertView的复用   第一种优化就是重用convertView,这也是最简单的一种优化方式,就是在Adapter类的getView方法中通过判 ...

  4. C语言快速排序算法及三种优化方式

    C语言快速排序算法及三种优化方式 C语言快速排序算法及三种优化方式 原理 快速排序复杂度分析 1 时间复杂度 2 空间复杂度 快速排序代码实现 1 普通快速排序 2 快速排序优化1-三数取中优化不必要 ...

  5. m对比PSO,WPA,GWPA以及GWO四种优化算法的优化性能,优化目标函数为10个来自CEC2017的标准测试函数

    目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 灰狼优化算法(GWO),灵感来自于灰狼.GWO算法模拟了自然界灰狼的领导层级和狩猎机制.四种类型的灰 ...

  6. php主要算法设计,四种排序算法设计(PHP)

    标签 详细分析 /** * 四种排序算法设计(PHP) * * 1) 插入排序(Insertion Sort)的基本思想是: 每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当 ...

  7. 数据结构与算法 | 二叉树四种的遍历方法(递归与非递归)

    二叉树的遍历是指从根节点出发,按照某种次序依次访问二叉树的所有节点,使得每个节点被访问且只访问一次. 而一般有四种遍历方法: 前序.中序.后序.层序,下面就分别讲一下四个遍历的思路以及代码实现 例如我 ...

  8. 算法-22-字符串的排序算法(四种排序)

    目录 1.字符串 1.1.属性 1.2.字母表 2.字符串排序方法 3.键索引计数法 3.1.第一步:频率统计 3.2.第二步:将频率转换为索引 3.3.第三步:数据分类排序 3.4.第四步:回写排序 ...

  9. 9种排序算法在四种数据分布下的速度比较

    9种算法分别是: 1.选择排序 2.希尔排序 3.插入排序 4.归并排序 5.快速排序 6.堆排序 7.冒泡排序 8.梳排序 9.鸡尾酒排序 在不同的情形下,排序速度前三名也不尽相同 Random   ...

最新文章

  1. postgresql开发中可能有用的知识
  2. visualstudio发布网站到服务器,发布到网站 - Visual Studio (Windows) | Microsoft Docs
  3. 达达O2O后台架构演进实践:从0到4000高并发请求背后的努力!
  4. HTTP协议常用标准状态码含义
  5. 计算机网络中什么叫总衰耗_1、什么是计算机网络?
  6. 学习笔记(十九)——Python与数据库交互(mysql、redis)
  7. VS中安装DevExpress后在Winform的工具箱中不显示控件
  8. boost::gil::view_is_basic用法的测试程序
  9. 文献学习(part19)--Graph Connectivity In Sparse Subspace Clustering
  10. 02-1.CSS边框,边界,布局相关笔记
  11. Flutter中ListView加载图片数据的优化
  12. mybatisplus的逻辑删除
  13. 矩阵的内积和外积,三向量混合积
  14. 单精度与双精度是什么意思,有什么区别?
  15. 管理学二(学习与沟通的重要性)
  16. Juju-maas 环境搭建
  17. 【黑马程序员pink老师前端】JavaScript对象
  18. carbondata 安装文档
  19. 最全防雷器电路及保护电路解析
  20. 嵌入式linux学习笔记-- 对于动态库的一些操作 dlopen

热门文章

  1. 手把手教会你:VMware Esxi系统安装步骤(版本7.0.3)
  2. RTL8720CM WI-FI+蓝牙,低功耗IoT(物联网)应用 40QFN
  3. CSDN周赛第39期题解
  4. Java list转set;JDK8 下list 集合转Set 集合
  5. Demo_C#_Winform Ftp异步下载文件,更新ProgressBar
  6. PTA 浪漫侧影 Python
  7. 关于VR视频的分辨率、码率、帧率
  8. 工作两年自我介绍程序员,不愧是大佬
  9. Python----方法返回None值报错 TypeError NoneType object is not callable
  10. ‘NoneType‘ object is not iterable