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. xpath加PHP对网站相关数据的截取

    首先了解一串代码 <?php  $url = 'http://www.baidu.com';$ch = curl_init();curl_setopt($ch, CURLOPT_FILE, fo ...

  2. PHP导出excel文件,第一步先实现PHP模板导出不带数据

    今天继续研究PHP导出excel文件,把复杂的事情简单化,一步步实现功能,首先实现模板文件的导出,随后再实现写入数据后导出,最终实现功能,这是基本思路.中间可以加一步,先自己写入数据导出试试,随后再数 ...

  3. android的布局 (如何实现空心圆效果的布局)

    layer-list : 简单来说layer-list就是图层列表的意思,是用来创建LayerDrawable的,LayerDrawable是DrawableResource的一种,所以,layer- ...

  4. [hdu3484]枚举

    题意:给两个个01矩阵,有两种操作,(1)交换两列(2)反转某一行.求能否通过若干操作使两矩阵相等 思路:(把所有对B的操作放到A上来,这一定是可以做到一样的效果的)枚举B矩阵的第一列对应A矩阵的第几 ...

  5. python --RecursionError: maximum recursion depth exceeded in comparison

    在学习汉娜塔的时候,遇到一个error RecursionError: maximum recursion depth exceeded in comparison 经过百度,百度的方法: 加上: i ...

  6. Android 组件间通信--事件驱动

    在android中,组件间通信常用的方式: 1.使用广播机制:在主页面中监听特定的广播事件,进行业务逻辑的操作,其他页面只需要根据需求发送广播即可 例如:常用app结构中,左边通常为菜单栏,点击菜单栏 ...

  7. Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  8. C++语言字符串处理函数

    C++语言提供了比C语言更丰富的字符串处理功能.它可以在字符串上经行输入,输出,合并,修改,比较,转换,复制,搜索等操作.使用这些现成的功能可以大大减少我们的编程的负担. 输入和输出的字符串函数,如p ...

  9. 14.3 Go iris

    14.3 Go iris 下载 go get -u -v github.com/kataras/iris 代码示例 package main import "github.com/katar ...

  10. 11.1 Go Http

    11.0 Go Http http客户端 package main import ( "fmt" "net/http" "net/http/httpu ...