我们用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。

如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。

期望时间复杂度:O(me), 其中m为所有顶点进队的平均次数,可以证明m一般小于等于2:“算法编程后实际运算情况表明m一般没有超过2n.事实上顶点入队次数m是一个不容易事先分析出来的数,但它确是一个随图的不同而略有不同的常数.所谓常数,就是与e无关,与n也无关,仅与边的权值分布有关.一旦图确定,权值确定,原点确定,m就是一个确定的常数.所以SPFA算法复杂度为O(e).证毕."(SPFA的论文)不过,这个证明是非常不严谨甚至错误的,事实上在bellman算法的论文中已有这方面的内容,所以国际上一般不承认SPFA算法。

#include<iostream>
#include<vector>
#include<deque>
using namespace std;
struct Edge
{
int to,length;
};
bool spfa(const int &beg,//出发点
const vector<vector<Edge> > &adjlist,//邻接表,通过传引用避免拷贝
vector<int> &dist,//出发点到各点的最短路径长度
vector<int> &path)//路径上到达该点的前一个点
//C++习惯上函数异常返回非零值,未异常才返回0(想想main函数),因此出现负权回路返回1!
//福利:这个函数没有调用任何全局变量,可以直接复制!
{
const int &INF=0x7FFFFFFF,&NODE=adjlist.size();//用邻接表的大小传递顶点个数,减少参数传递
dist.assign(NODE,INF);//初始化距离为无穷大
path.assign(NODE,-);//初始化路径为未知
deque<int> que(,beg);//(双端)处理队列
vector<bool> flag(NODE,);//标志数组,判断是否在队列中
vector<int> cnt(NODE,);//记录各点入队次数,用于判断负权回路
dist[beg]=;//出发点到自身路径长度为0
++cnt[beg];//开始计数
flag[beg]=;//入队
while(!que.empty())
{
const int now=que.front();//当前处理的点,由于后面被删除,不可定义成常量引用
que.pop_front();
flag[now]=;//将该点拿出队列
for(int i=; i!=adjlist[now].size(); ++i)//遍历所有与当前点有路径的点
{
const int &next=adjlist[now][i].to;//目标点,不妨定义成常量引用,稍稍快些
if(dist[now]<INF&&//若距离已知(否则下面右式计算结果必爆int),且
//注:与运算先判断左式是否成立,若不成立则右式不会被判断
dist[next]>dist[now]+adjlist[now][i].length)//优于当前值
{
dist[next]=dist[now]+adjlist[now][i].length;//更新
path[next]=now;//记录路径
if(!flag[next])//若未在处理队列中
{
if(++cnt[next]==NODE)return ;//计数后出现负权回路
if(que.empty()||//空队列,或(或运算实现原理类似与运算)
dist[next]<dist[que.front()])//优先级高于队首(SLF)
que.push_front(next);//放在队首
else que.push_back(next);//否则放在队尾
flag[next]=;//入队
}
}
}
}
return ;
}
int main()
{
int n_num,e_num,beg;//含义见下
cout<<"输入点数、边数、出发点:";
cin>>n_num>>e_num>>beg;
vector<vector<Edge> > adjlist(n_num,vector<Edge>());//默认初始化邻接表
for(int i=,p; i!=e_num; ++i)
{
Edge tmp;
cout<<"输入第"<<i+<<"条边的起点、终点、长度:";
cin>>p>>tmp.to>>tmp.length;
adjlist[p].push_back(tmp);
}
vector<int> dist,path;//用于接收最短路径长度及路径各点
if(spfa(beg,adjlist,dist,path))cout<<"图中存在负权回路\n";
else for(int i=; i!=n_num; ++i)
{
cout<<beg<<"到"<<i<<"的最短距离为"<<dist[i]<<",反向打印路径:";
for(int w=i; path[w]>=; w=path[w])
cout<<w<<"<-";
cout<<beg<<'\n';
}
}

最新文章

  1. emacs不能使用中文输入法
  2. 【总结】清除webbrowser cookie/session的6种方法
  3. HTML Agility Pack 搭配 ScrapySharp,彻底解除Html解析的痛苦
  4. mysql取出现在的时间戳和时间时间戳转成人类看得懂的时间
  5. 关于用phonegap+jquery moblie开发 白屏闪屏的解决方法
  6. [实变函数]3.1 外测度 (outer measure)
  7. HTML5 开发APP(MUI的一些特性)
  8. ubuntu12.04destdrop删除不必要的软件
  9. Node笔记三
  10. 20秒教你如何写maven2的pom文件的依赖包
  11. vim创建程序文件自动添加头部注释/自动文件头注释与模板定义
  12. 主机连接虚拟机redis 服务器
  13. pycharm 设置参数,快捷键
  14. 在 OC 中调用 Swift 代码
  15. DataGrip激活码
  16. Java 读取Excel 文件内容
  17. Docker swarm集群搭建教程
  18. Delphi 包的设计思想及它与PAS、BPL、DCU、DLL、OXC的关系
  19. Android 关于导航栏(虚拟按键)遮挡PopupWindow底部布局的问题
  20. python随机数,随机选择……random

热门文章

  1. OpenCV Using Python——基于SURF特征提取和金字塔LK光流法的单目视觉三维重建 (光流、场景流)
  2. MySQL Crash Course #13# Chapter 21. Creating and Manipulating Tables
  3. 【翻译】std::list::remove - C++ Reference
  4. 函数对象与仿函数(function object and functor)
  5. 20145317《网络对抗》shellcode注入&amp;Return-to-libc攻击深入
  6. Unity3D学习笔记(二十一):InputFiled、Dropdown、Scroll Rect、Mask
  7. 【TCP/IP详解 卷一:协议】第二章:链路层
  8. 初识C++继承
  9. JS进阶系列之内存空间
  10. React Native的SliderIOS滑块组件