2018/1/28 每日一学 单源最短路的SPFA算法以及其他三大最短路算法比较总结
刚刚AC的pj普及组第四题就是一种单源最短路。
我们知道当一个图存在负权边时像Dijkstra等算法便无法实现;
而Bellman-Ford算法的复杂度又过高O(V*E),SPFA算法便派上用场了。
其实SPFA 是用队列的优化,过程详见下图(PS:图片转自网络)


好了,以上图片基本已经说明的SPFA的过程,下面就是代码实现:
模板如下:
void spfa(){
; i<=n; i++) dis[i]=INF; //初始化
dis[start]=; inq[start]=;
q.push(start);
int i, v;
while (!q.empty){
v=q.front(); // 取队首节点
q.pop();
inq[v]=; //释放节点,因为这节点可能下次被其他节点松弛,重新入队
; i<=n; i++) //枚举所有顶点
&& dis[i]>dis[v]+a[v][i]){ //判断
dis[i] = dis[v]+a[v][i]; //修改
if (!inq[i]){ // 如果扩展结点i不在队列中,入队
q.push(i);
vis[i]=;
}
}
}
}
可以看到,因为维护队列,和bfs有其曲同工之妙,但有一点不同!!!
bfs一旦入队,哪怕后面出队也无法在入队,而SPFA不同。
从数组名vis[i](BFS),inq[i](SPFA)可以看出定义不同。
那么对于有负权边,SPFA时间会大大增加……
不难想到DFS会不会快一点(好吧,既然都说了,肯定快,233)。
大约是O(E)。
代码如下:
void spfa(now){//DFS
; i<=edge[now]; i++) //枚举从顶点now发出的边
if (dis[to[now][i]>dis[now]+a[now][to[now][i]]){
dis[to[now][i]=dis[now]+a[now][to[now][i]];
spfa(to[now][i]);//继续DFS
}
}
我们知道DFS其实是遍历到终点才换成另一条路,因此可以用来判断负权边!!
只需判断是否回到之前的节点即可,可以用 vis[i] bool数组记录。
再看看Bellman-Ford算法,思路太简单,枚举点和边,就是时间比较长,为O(VE)。
代码如下:(转自百度百科)
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 0x3f3f3f3f
#define N 1010
int nodenum, edgenum, original; //点,边,起点
typedef struct Edge //边
{
int u, v;
int cost;
}Edge;
Edge edge[N];
int dis[N], pre[N];
bool Bellman_Ford()
{
; i <= nodenum; ++i) //初始化
dis[i] = (i == original ? : MAX);
; i <= nodenum - ; ++i)
; j <= edgenum; ++j)
if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) //松弛(顺序一定不能反~)
{
dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;
pre[edge[j].v] = edge[j].u;
}
; //判断是否含有负权回路
; i <= edgenum; ++i)
if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost)
{
flag = ;
break;
}
return flag;
}
void print_path(int root) //打印最短路的路径(反向)
{
while(root != pre[root]) //前驱
{
printf("%d-->", root);
root = pre[root];
}
if(root == pre[root])
printf("%d\n", root);
}
int main()
{
scanf("%d%d%d", &nodenum, &edgenum, &original);
pre[original] = original;
; i <= edgenum; ++i)
{
scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost);
}
if(Bellman_Ford())
; i <= nodenum; ++i) //每个点最短路
{
printf("%d\n", dis[i]);
printf("Path:");
print_path(i);
}
else
printf("have negative circle\n");
;
}
看到核心部分,不难想到外层i跟内层循环无关,因此可以优化,即如果内层无松弛,可以提前结束!
这样一来,速度还是可以的……
之后我们看看dijkstra算法,其实就是贪心。
dis数组用来储存起始点到其他点的最短路。
转移方程为:
dis[i]=min(dis[i],dis[j]+w[j][i]|j为i能到达的点)
一开始dis[i]=INF,dis[start]=0;
很显然,不能处理有负边的情况……
时间为(V^2).两层循环解决。
注意每次选用没更新过的离源点最近的点对外拓展。
代码如下:
#include<stdio.h>
#include<stdlib.h>
#define INF 1<<28
#define N 1000+5
int a[N][N];
int d[N];
bool vis[N];
int i,j,k;
int m;//m代表边数
int n;//n代表点数
int main()
{
scanf("%d%d",&n,&m);
int mn;
int x,y,z;
;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
a[x][y]=a[y][x]=z;
}
;i<=n;i++)
d[i]=INF;
;i<=m;i++)
{
mn=INF;
;j<=n;j++)
if(!vis[j]&&d[j]<mn)
{
mn=d[j];
k=j;
}
vis[k]=;
;j<=n;j++)
&&d[j]>d[k]+a[k][j])
d[j]=d[k]+a[k][j];
}
;i<=n;i++)
printf("%d ",d[i]);
;
}
最后用最最最最……最智障的floyd算法结束今天学习(完全是为了凑齐四种算法,基本没啥可说)
直接看核心代码
; k<=n; k++)
; i<=n; i++)
; j<=n; j++)
{
if(w[i][j]>w[i][k]+w[k][j])
w[i][j]=map1[i][k]+w[k][j];
}
注意最外层是循环中间的点!!!
其他就比较简单,不解释了,ok!
2018/1/28 每日一学 单源最短路的SPFA算法以及其他三大最短路算法比较总结的更多相关文章
- 2018/03/08 每日一学PHP 之 常量defind 和 const区别
常量defind 和 const区别 什么是常量? 如字面理解的,在脚本执行期间不可改变的的量. 定义一个常量应该注意的事项? 1:常量默认大小写敏感,错误的大小写不会被识别为常量. 2:常量只能是标 ...
- 2018/1/27 每日一学 最长不降序子序列的O(n*logn)算法
手动维护一个数组模拟即可,233-- 可以使用algorithm中的lower_bound(相当于二分) 代码如下: #include<cstdio> #include<algori ...
- 2018/05/02 每日一学Linux 之 .bash_profile和.bashrc的区别
最近一直在学习其他,导致博客就疏忽了,很不好(其实就是自己懒了......). -- 为什么要使用 .bash_profile和.bashrc ? 在平常的使用中,有些文件夹或者命令很长,在执行时需要 ...
- 2018/04/18 每日一学Linux 之 ssh关闭密码登录
在平常工作中,常常需要关闭 SSH 的密码登录,只留 SSH 证书登录. 好处显而易见,避免了经常输入密码导致的密码泄露,和设置密码导致被暴力破解的可能性. -- 方法也很简单,首先 你是可以 登录 ...
- 2018/03/28 每日一个Linux命令 之 mkdir/rmdir
用于建立空文件夹和删除文件夹 -- 两命令重要参数 -p 递归建立/删除 -- 例如 mkdir -p demo1/demo2/demo3 建立demo3空文件夹,如果demo1/demo2没建立也建 ...
- 2018/03/10 每日一学PHP 之 修饰符 public/private/protected
对于面向对象 修饰符的使用是我们最常用,也是很容易忽略的小细节. 对于编程来说,把握好每一个小细节,就能构造出漂亮,优雅的程序. public 使用最多的修饰符,公共方法,允许所有访问,就像一个公交车 ...
- 2018/03/09 每日一学PHP 之 require_once require include include_once 包含文件的区别
require_once require include include_once 方法的区别 对于包含文件来说,如果只是使用框架来说的话,应该会很少碰到,因为框架底层对于文件的引用等做了很好的封装, ...
- 【luogu P3371 单源最短路径 】 模板 SPFA优化
无优化:500ms deque优化:400ms #include <queue> #include <cstdio> #include <cstring> #inc ...
- 【luogu P3371 单源最短路径】 模板 SPFA
题目链接:https://www.luogu.org/problemnew/show/P3371 我永远都喜欢Flyod.dijkstra + heap.SPFA #include <cstdi ...
随机推荐
- [Spark性能调优] 第三章 : Spark 2.1.0 中 Sort-Based Shuffle 产生的内幕
本課主題 Sorted-Based Shuffle 的诞生和介绍 Shuffle 中六大令人费解的问题 Sorted-Based Shuffle 的排序和源码鉴赏 Shuffle 在运行时的内存管理 ...
- JS ajaxfileUpload 一次性上传多个input控件 上传多个文件
本方法适用于一次性上传多个input框输入的文件,如下图所示,任务是需要一次上传两个input框提供的两个文件. 具体方法: 1.修改ajax调用方法 如上图所示,只需要将ajaxFileUpload ...
- Linux CentOS 6.5 配置网络
网卡说明 第一块网卡为配置外网:eth0 第二块网卡为配置内网:eth1(没有外网的机器也要将内网配置在第二块网卡上) 1.使用ifconfig查看网卡配置信息 2.修改网卡1配置文件/etc/sys ...
- DirectSound---输出设备基本操作(枚举、查询等)
DirectSound是DirectX组件之一,提供了对音频设备的捕获和播放能力,同时它也是唯一几个支持Xp系统的音频技术之一. DirectSound主要有以下特点: 优点: 播放音频低延迟. 硬件 ...
- 【转载】netstat--查看服务器[有效]连接数--统计端口并发数--access.log分析
简介 Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Member ...
- 编写Qt Designer自定义控件
一)流程概述 在使用Qt Designer设计窗体界面时,我们可以使用Widget Box里的窗体控件非常方便的绘制界面,比如拖进去一个按钮,一个文本编辑器等.虽然Qt Designer里的控件可以满 ...
- 用json方法来作深拷贝应该知道的一点东西!
之前写js比较多的的时候也写过深拷贝,浅拷贝,继承啥的,还有自定义的监听事件.然而过了很久都忘了. 最近在项目上用的深拷贝都是 b = JSON.parse( JSON.stringify(a) ) ...
- Java与算法之(1) - 冒泡排序
冒泡排序法的原理是,每次比较相邻的两个元素,如果它们的顺序错误就把它们交换过来. 例如对4 3 6 2 7 1 5这7个数字进行从小到大的排序,从最左侧开始,首先比较4和3 因为是从小到大排序,4和3 ...
- 【请您听我说】PHP语法特点的一些看法
一.基本认识 PHP是干什么的?百度百科上提到说:PHP就是一门脚本语言,开发用的,相信这个你们只要去搜一下,就会有一大堆关于PHP概念的解释. 相信我们对PHP的初步认识是从浏览器开始的吧,当我们每 ...
- hdu_1025(LIS Nlog(N)算法)
题意:自己慢慢读吧.大概就是道路两边建路,给出建路需求,要求两条路不能有交叉,问最多可以建多少条路. 题解:一看数据范围500000,应该是dp,再画个图模拟一下,发现实质就是求最长上升子序列,很自然 ...