SPFA是经过对列优化的bellman-Ford算法,因此,在学习SPFA算法之前,先学习下bellman-Ford算法。

bellman-Ford算法是一种通过松弛操作计算最短路的算法。

适用条件

1.单源最短路径(从源点s到其它所有顶点v);
2.有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图);
3.边权可正可负(如有负权回路输出错误提示);
4.差分约束系统;

bellman-Ford的具体操作是这样的:

  1. 初始化,dis数组表示从起点到达第i个点的最短距离。初始化时:dis[i]=edge[起点][i];如果没有边相接,则设为MAXN;
  2. 循环n-1次,遍历每个边,将dis[边[i].目标节点]=min(dis[边[i].目标节点],dis[边[i].初始节点]+边[i].权值);
  3. 【可选】检验负权回路:判断边集合中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 dis[v]中。
#include <bits/stdc++.h>
using namespace std;
int n,m;
//Bellman-Ford algorithm
struct Edge
{
int a,b,v;
}edge[];
long long dis[];
void bellman_ford(int x)
{
dis[x]=;
for(int i=;i<=n-;i++)
{
for(int j=;j<=m;j++)
{
dis[edge[j].b]=min(dis[edge[j].b],dis[edge[j].a]+edge[j].v);
}
}
return ;
/*
bool flag = 1; //判断是否含有负权回路
for(int i = 1; i <= m; ++i) if(dis[edge[i].b] > dis[edge[i].a] + edge[i].cost)
{
flag = 0;
break;
}
return flag;
*/
}
int main()
{
int s;
cin>>n>>m>>s;
for(int i=;i<=m;i++)
{
scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].v);
}
for(int i=;i<=n;i++)dis[i]=;
bellman_ford(s);
for(int i=;i<=n;i++)
cout<<dis[i]<<" ";
return ;
}

这个算法在luogu上70分,TLE了两个测试点,因此需要学习一个更牛逼的算法,SPFA,听说这是一个交大的教授发明的。附上原论文连接:https://wenku.baidu.com/view/df249954d4d8d15abe234eff.html

SPFA算法的全称是:Shortest Path Faster Algorithm,是西南交通大学段凡丁于 1994 年发表的论文中的名字。不过,段凡丁的证明是错误的,且在 Bellman-Ford 算法提出后不久(1957 年 )已有队列优化内容,所以国际上不承认 SPFA 算法是段凡丁提出的。

为了避免最坏情况的出现,在正权图上应使用效率更高的Dijkstra算法。若给定的图存在负权边,类似Dijkstra算法等算法便没有了用武之地,SPFA算法便派上用场了。

简洁起见,我们约定加权有向图G不存在负权回路,即最短路径一定存在。用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
 
定理:只要最短路径存在,上述SPFA算法必定能求出最小值。证明:每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。
#include <bits/stdc++.h>
using namespace std;
int n,m,start;
long long dis[],used[],head[];
int num_edge=;
struct Graph
{
int to,w,next;
}graph[];
bool spfa(int x)
{
queue <int> q;
dis[x]=;
used[x]=;
q.push(x);
while(!q.empty())
{
int u=q.front();
q.pop();
used[u]=;
for(int i=head[u];i;i=graph[i].next)
{
int v=graph[i].to;
long long Songchi=dis[u]+graph[i].w;
if(dis[v]>Songchi)
{
dis[v]=Songchi;
if(used[v]==)
{
used[v]=;
q.push(v);
}
}
}
}
return ;
}
void addedge(int from,int to,int dis)//邻接表存边
{
graph[++num_edge].next=head[from];
graph[num_edge].to=to;
graph[num_edge].w=dis;
head[from]=num_edge;
}
int main()
{
memset(head,,sizeof(head));
memset(used,,sizeof(used));
cin>>n>>m>>start;
for(int i=;i<=n;i++)dis[i]=;
for(int i=;i<=m;i++)
{
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
addedge(a,b,w);
}
if(spfa(start))
{
for(int i=;i<=n;i++)
cout<<dis[i]<<" ";
}
}

【算法】单元最短路径之Bellman-Ford算法和SPFA算法的更多相关文章

  1. 0-1背包的动态规划算法,部分背包的贪心算法和DP算法------算法导论

    一.问题描述 0-1背包问题,部分背包问题.分别实现0-1背包的DP算法,部分背包的贪心算法和DP算法. 二.算法原理 (1)0-1背包的DP算法 0-1背包问题:有n件物品和一个容量为W的背包.第i ...

  2. 【数据结构与算法】字符串匹配(Rabin-Karp 算法和KMP 算法)

    Rabin-Karp 算法 概念 用于在 一个字符串 中查找 另外一个字符串 出现的位置. 与暴力法不同,基本原理就是比较字符串的 哈希码 ( HashCode ) , 快速的确定子字符串是否等于被查 ...

  3. 最短路径——Dijkstra算法和Floyd算法

    Dijkstra算法概述 Dijkstra算法是由荷兰计算机科学家狄克斯特拉(Dijkstra)于1959 年提出的,因此又叫狄克斯特拉算法.是从一个顶点到其余各顶点的最短路径算法,解决的是有向图(无 ...

  4. 最短路径Dijkstra算法和Floyd算法整理、

    转载自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html 最短路径—Dijkstra算法和Floyd算法 Dijks ...

  5. 【转】最短路径——Dijkstra算法和Floyd算法

    [转]最短路径--Dijkstra算法和Floyd算法 标签(空格分隔): 算法 本文是转载,原文在:最短路径-Dijkstra算法和Floyd算法 注意:以下代码 只是描述思路,没有测试过!! Di ...

  6. 最小生成树---Prim算法和Kruskal算法

    Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (gra ...

  7. 最小生成树——Prim算法和Kruskal算法

    洛谷P3366 最小生成树板子题 这篇博客介绍两个算法:Prim算法和Kruskal算法,两个算法各有优劣 一般来说当图比较稀疏的时候,Kruskal算法比较快 而当图很密集,Prim算法就大显身手了 ...

  8. prim 算法和 kruskal算法

    Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (gra ...

  9. 词性标注算法之CLAWS算法和VOLSUNGA算法

    背景知识 词性标注:将句子中兼类词的词性根据上下文唯一地确定下来. 一.基于规则的词性标注方法 1.原理 利用事先制定好的规则对具有多个词性的词进行消歧,最后保留一个正确的词性. 2.步骤 ①对词性歧 ...

随机推荐

  1. C. Yet Another Counting Problem(循环节规律)

    \(给出a,b,l,r,求在区间[l,r]内有多少x满足x%a%b!=x%b%a\) \(--------------------分割!!~----------------------------\) ...

  2. dp 20190617

    A. Alternative Thinking 这个标的是dp,但是我感觉就只能算思维题,不是特别难, 你仔细想想就知道,你先求出01这样子满足条件的个数,如果要进行改变,最多只可以增加两个,也可以增 ...

  3. git&&SourceTree使用总结

    git&&sourceTree操作学习 基本操作 commit 提交 pull 更新代码 push 推送代码 fetch 抓取代码 Branch 新建分支 merge 合并代码 Sta ...

  4. Spring Cloud Stream学习(五)入门

    前言: ​ 在了解完RabbitMQ后,再来学习SpringCloudStream就轻松很多了,SpringCloudStream现在主要支持两种消息中间件,一个是RabbitMQ,还有一个是KafK ...

  5. LeetCode--Unique Email Addresses & Hamming Distance (Easy)

    929. Unique Email Addresses (Easy)# Every email consists of a local name and a domain name, separate ...

  6. x86软路由虚拟化openwrt-koolshare-mod-v2.33联通双拨IPV6教程(第二篇)

    续第一篇:https://www.cnblogs.com/zlAurora/p/12433296.html   4 设置多拨 (1)连入OpenWrt Web界面,默认为192.168.1.1,在“网 ...

  7. 【Hadoop离线基础总结】日志采集框架Flume

    日志采集框架Flume Flume介绍 概述 Flume是一个分布式.可靠.和高可用的海量日志采集.聚合和传输的系统.它可以采集文件,socket数据包.文件.文件夹.kafka等各种形式源数据,又可 ...

  8. 手机网页,div内滚动条,以及div内部滚动条拉到底部之后触发事件

    var gao = document.documentElement.clientHeight; var headHeight = parseInt($('.yhead').css('height') ...

  9. masonry中的make,remake,update

    - (void)viewDidLoad { [super viewDidLoad]; self.navigationController.navigationBar.translucent = NO; ...

  10. angular 实现依赖注入

    1:首先获取module对象var myAppModule = angular.module('myApp', []); 2:定义对象(类似spring中xml声明bean对象<bean id= ...