一.Floyd算法

  用于计算任意两个节点之间的最短路径。

参考了five20的博客

Floyd算法的基本思想如下:从任意节点A到任意节点B的最短路径不外乎2种可能,1是直接从A到B,2是从A经过若干个节点到B,所以,我们假设dist(AB)为节点A到节点B的最短路径的距离,对于每一个节点K,我们检查dist(AK) + dist(KB) < dist(AB)是否成立,如果成立,证明从A到K再到B的路径比A直接到B的路径短,我们便设置 dist(AB) = dist(AK) + dist(KB),这样一来,当我们遍历完所有节点K,dist(AB)中记录的便是A到B的最短路径的距离。

标准五行代码如下:

for(k=;k<=n;k++)
for(i=;i<=n;i++)
for(j=;j<=n;j++) {
if(dis[i][k]+dis[k][j]<dis[i][j]) {
dis[i][j]=dis[i][k]+dis[k][j];
}
}

  但是这里我们要注意循环的嵌套顺序,如果把检查所有节点K放在最内层,那么结果将是不正确的,为什么呢?因为这样便过早的把i到j的最短路径确定下来了,而当后面存在更短的路径时,已经不再会更新了。

  

  更多关于Floyd算法,详细请见:Floyd算法百度百科链接

二.Dijkstra算法:

  适用于权值为非负的图的单源最短路径。

  斐波那契堆优化的时间复杂度为O(E+VlogV),但其实只是理论值,实际中基本上达不到。

  算法思路:

  参考了殷天文的博客

  • 指定一个节点,例如我们要计算 'A' 到其他节点的最短路径

  • 引入两个集合(S、U),S集合包含已求出的最短路径的点(以及相应的最短长度),U集合包含未求出最短路径的点(以及A到该点的路径,注意 如上图所示,A->C由于没有直接相连 初始时为∞

  • 初始化两个集合,S集合初始时 只有当前要计算的节点,A->A = 0

    U集合初始时为 A->B = 4, A->C = ∞, A->D = 2, A->E = ∞敲黑板!!!接下来要的两个步骤是核心!

  • 从U集合中找出路径最短的点,加入S集合,例如 A->D = 2

  • 更新U集合路径,if ( 'D 到 B,C,E 的距离' + 'AD 距离' < 'A 到 B,C,E 的距离' ) 则更新U

  • 循环执行 4、5 两步骤,直至遍历结束,得到A 到其他节点的最短路径

  朴素版时间复杂度O(n²)算法代码如下:

void Dijkstra()
{
memset(dis, 0x1f, sizeof(dis));
dis[] = ;
for (int i=; i<=n; i++) {
int min_len = 1e9, k = ;
for (int j=; j<=n; j++)
if (!vis[j] && dis[j] < min_len) {
min_len = dis[j];
k = j;
}
vis[k] = ;
for (int j=h[k]; j!=-; j=edge[j].next) {
int to = edge[j].to, w = edge[j].w;
if (!vis[to] && dis[to] > dis[k]+w) {
dis[to] = dis[k]+w;
}
}
}
}

  稳定时间复杂度O(mlogn)的堆优化(优先队列代替)代码如下:

void Dijkstra_Heap()
{
priority_queue<pair<int, int> > q;
memset(dis, 0x3f, sizeof(dis));
memset(vis,,sizeof(v));
dis[] = ;
q.push(make_pair(, ));
while (!q.empty()) {
int now = q.top().second;
q.pop();
if (vis[now]) continue;
vis[now] = ;
for (int i=h[now]; i!=-; i=edge[i].next) {
int to = edge[i].to, w = edge[i].w;
if (!vis[to] && dis[to]>dis[now]+w) {
dis[to] = dis[now] + w;
q.push(make_pair(-dis[to], to));
}
}
}
}

这里有一个关于优先队列的小骚操作:

  由于STL中的优先队列默认是大根堆,所以在使用push函数的时候只需在需要排序的数据前加个‘-’即可。

 

  关于Dijkstra算法的选择上,对于稀疏图,由于n和m比较接近,故选择堆优化算法;而对于稠密图,由于点少边多,故选择朴素版算法

  更多关于Dijkstra算法,详细请见:Dijkstra算法百度百科链接

  推荐博客:数据结构--Dijkstra算法最清楚的讲解

三.Bellman-Ford算法

  参考博客:图解贝尔曼福特-算法

可用于解决以下问题:

  • 从A出发是否存在到达各个节点的路径(有计算出值当然就可以到达);
  • 从A出发到达各个节点最短路径(时间最少、或者路径最少等)
  • 图中是否存在负环路(权重之和为负数)
  • 有边数限制的最短路

  算法思路:

  1.  初始化时将起点s到各个顶点v的距离dist(s->v)赋值为∞,dist(s->s)赋值为0
  2.  后续进行最多n-1次遍历操作,对所有的边进行松弛操作,假设:
  3.  所谓的松弛,以边ab为例,若dist(a)代表起点s到达a点所需要花费的总数, dist(b)代表起点s到达b点所需要花费的总数,weight(ab)代表边ab的权重, 若存在:
  4.  (dist(a) +weight(ab)) < dist(b)
  5.  则说明存在到b的更短的路径,s->...->a->b,更新b点的总花费为(dist(a) +weight(ab)),父节点为a
  6.  遍历都结束后,若再进行一次遍历,还能得到s到某些节点更短的路径的话,则说明存在负环路

  思路上与狄克斯特拉算法(Dijkstra algorithm)最大的不同是每次都是从源点s重新出发进行"松弛"更新操作,而Dijkstra则是从源点出发向外扩逐个处理相邻的节点,不会去重复处理节点,这边也可以看出Dijkstra效率相对更高点。

  下面是有边数k限制的Bellman_ford算法模板:

void Bellman_ford()
{
memset(dis,0x1f,sizeof(dis));
dis[]=;
for(int i=;i<=k;i++)
{
memcpy(last,dis,sizeof(last));
for(int j=;j<=m;j++)
{
int u=edge[j].u,v=edge[j].v,w=edge[j].w;
if(dis[v]>last[u]+w)dis[v]=last[u]+w;
}
}
}

  

  更多关于Bellman-Ford算法,详细请见:Bellman-Ford算法百度百科链接

四.SPFA算法

  SPFA是西安交通大学的段凡丁在1994年与《西安交通大学学报》中发表的“关于最短路径的SPFA快速算法”,他在里面说SPFA速度比Dijkstra快,且运行V次的SPFA速度比Floyd速度快。
  而事实证明SPFA算法是有局限的,他不适用于稠密图,对于特别情况的稠密图,SPFA复杂度和BellmanFord时间一样。

  适用范围:适用于权值有负值,且没有负圈的图的单源最短路径,论文中的复杂度O(kE),k为每个节点进入Queue的次数,且k一般<=2,但此处的复杂度证明是有问题的,其实SPFA的最坏情况应该是O(VE).

  注意:SPFA算法在网格图中非常慢

  算法思路:

  参考了小天位的博客

  •  SPFA(Shortest Path Faster Algorithm) [图的存储方式为邻接表]。
  •  是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。
  •  算法大致流程是用一个队列来进行维护。 初始时将源加入队列。 每次从队列中取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。 直到队列为空时算法结束。它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。
  • SPFA 在形式上和BFS非常类似,不同的是BFS中一个点出了队列就不可能重新进入队列,但是SPFA中,一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本身被改进,于是再次用来改进其它的点,这样反复迭代下去。
  •  判断有无负环:如果某个点进入队列的次数超过V次则存在负环(SPFA无法处理带负环的图)。

  代码如下:

void SPFA() {
memset(dis, 0x3f, sizeof(dis));
memset(vis, , sizeof(vis));
queue<int> q;
dis[] = ; vis[] = ;
q.push();
while (!q.empty()) {
int now = q.front();
q.pop(); vis[now] = ;
for (int i=h[now]; i!=-; i=edge[i].next) {
int to = edge[i].to, w = edge[i].w;
if (dis[now]+w < dis[to]) { //在队列中的点也可以进行松弛操作,故这里不需加!ifq[to]的条件,而需要加在下面。
dis[to] = dis[now]+w;
if (!vis[to]) q.push(to), vis[to] = ;
}
}
}
}

  更多关于SPFA算法,详细请见:SPFA算法百度百科链接

五.最小环问题

  解决思路:

  最小环就是指在一张图中找出一个环,使得这个环上的各条边的权值之和最小。在Floyed的同时,可以顺便算出最小环。

  记两点间的最短路为dis[i][j],g[i][j]为边<i,j>的权值。 

  一个环中的最大结点为k(编号最大),与它相连的两个点为i,j,这个环的最短长度为g[i][k]+g[k][j]+(i到j的路径中,所有结点编号都小于k的最短路径长度)。

  根据Floyed的原理,在最外层循环做了k-1次之后,dis[i][j]则代表了i到j的路径中,所有结点编号都小于k的最短路径。

  综上所述,该算法一定能找到图中最小环。

  代码如下:

  参考了Coder_YX的博客

 void floyd(){
int MinCost = inf;
for(int k=;k<=n;k++){
for(int i=;i<k;i++)
for(int j=i+;j<k;j++)
MinCost = min(MinCost,dis[i][j]+mp[i][k]+mp[k][j]);//更新k点之前枚举ij求经过ijk的最小环
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); //更新k点
}
if(MinCost==inf)puts("It's impossible.");
else printf("%d\n",MinCost);
}

关于四种最短路算法的结论:

参考了xiazdong的博客

(1)当权值为非负时,用Dijkstra。
(2)当权值有负值,且没有负圈,则用SPFA,SPFA能检测负圈,但是不能输出负圈。
(3)当权值有负值,而且可能存在负圈,则用BellmanFord,能够检测并输出负圈。
(4)SPFA检测负环:当存在一个点入队大于等于V次,则有负环。

图论——最短路:Floyd,Dijkstra,Bellman-Ford,SPFA算法及最小环问题的更多相关文章

  1. (最短路径算法整理)dijkstra、floyd、bellman-ford、spfa算法模板的整理与介绍

    这一篇博客以一些OJ上的题目为载体.整理一下最短路径算法.会陆续的更新... 一.多源最短路算法--floyd算法 floyd算法主要用于求随意两点间的最短路径.也成最短最短路径问题. 核心代码: / ...

  2. 最短路(floyd/dijkstra/bellmanford/spaf 模板)

    floyd/dijkstra/bellmanford/spaf 模板: 1. floyd(不能处理负权环,时间复杂度为O(n^3), 空间复杂度为O(n^2)) floyd算法的本质是dp,用dp[k ...

  3. hdu 2066 ( 最短路) Floyd & Dijkstra & Spfa

    http://acm.hdu.edu.cn/showproblem.php?pid=2066 今天复习了一下最短路和最小生成树,发现居然闹了个大笑话-----我居然一直写的是Floyd,但我自己一直以 ...

  4. 图论--最短路--Floyd(含路径输出)

    #include<bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define maxn 1005 int D[ma ...

  5. hdoj 2544 最短路【dijkstra or spfa】

    最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  6. hdoj2544 最短路(Dijkstra || Floyd || SPFA)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2544 思路 最短路算法模板题,求解使用的Dijkstra算法.Floyd算法.SPFA算法可以当做求解 ...

  7. HDOJ 2544 最短路(最短路径 dijkstra算法,SPFA邻接表实现,floyd算法)

    最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  8. hdu1874 畅通project续 最短路 floyd或dijkstra或spfa

    Problem Description 某省自从实行了非常多年的畅通project计划后.最终修建了非常多路.只是路多了也不好,每次要从一个城镇到还有一个城镇时,都有很多种道路方案能够选择.而某些方案 ...

  9. 最短路(Dijkstra,Floyd,Bellman_Ford,SPFA)

    当然,这篇文章是借鉴大佬的... 最短路算法大约来说就是有4种——Dijkstra,Floyd,Bellman_Ford,SPFA 接下来,就可以一一看一下... 1.Dijkstra(权值非负,适用 ...

随机推荐

  1. bin文件夹下的某个dll总是自动刷新为不同版本的dll的解决方法

    如上图所示,一般这种问题都是dll版本和配置文件中的dll版本对应不上才引起的,可以通过替换对应版本的dll或者修改配置文件中的版本号即可. 然而我的情况是:修复后,还是不定时出现这样的问题,我以为是 ...

  2. zblog2.X 连不上数据库原因

    数据库配置Dim ZC_MSSQL_DATABASEZC_MSSQL_DATABASE="zb20" Dim ZC_MSSQL_USERNAMEZC_MSSQL_USERNAME= ...

  3. 解决IE8中select下拉列表文字上下不居中的问题

    对IE8及以下的浏览器设置padding属性,其他浏览器则设置line-height 属性

  4. selenium实现登录百度(自动识别简单验证码)

    需要做的工作 0.工程结构 1.代码: ①baidu_login.py import re import os import sys import time import random from se ...

  5. Oracle 多租户环境学习路线图

    Category Topic Documentation Concepts Overview of CDBs and PDBs "Overview of the Multitenant Ar ...

  6. RT-Thread--内核基础

    内核介绍 内核处于硬件层之上,内核部分包括内核库.实时内核实现. 实时内核的实现包括:对象管理.线程管理及调度器.线程间通信管理.时钟管理及内存管理等等,内核最小的资源占用情况是 3KB ROM,1. ...

  7. javascript获取用户按了哪个键

    浏览器好像不允许js获取F5这个键的按下事件,应该屏蔽了,这个键太过特殊,猜测可能是,防止用户失去对浏览器的控制? <!DOCTYPE html> <html> <hea ...

  8. Tcp/IP协议详讲

    TCP/IP协议分层详解 目录 TCP/IP 和 ISO/OSI TCP/IP分层模型 数据的封装与分用 其他相关概念 TCP/IP 通信传输流 负责传输的 IP 协议 正文 回到顶部 TCP/IP ...

  9. split()函数实现

    #split函数实现: ss='** *axx* *bv** *ctt** **dff***' result=[] def split_1(ss,a,times=len(ss)): i=0 n=0 w ...

  10. [转载]Appium工作原理

    [Appium]Appium工作原理 2017-09-13 15:28 sophia194910 阅读(7658) 评论(0) 编辑 收藏 参考:http://www.cnblogs.com/zhjs ...