前言

  • \(SPFA\)算法由于它上限 \(O(NM) = O(VE)\)的时间复杂度,被卡掉的几率很大.在算法竞赛中,我们需要一个更稳定的算法:\(dijkstra\).

什么是\(dijkstra\)?

  • \(dijkstra\)是一种单源最短路径算法,时间复杂度上限为\(O(n^2)\)(朴素),在实际应用中较为稳定\(;\)加上堆优化之后更是具有\(O((n+m)\log_{2}n)\)的时间复杂度,在稠密图中有不俗的表现.

\(dijkstra\)的原理/流程?

  • \(dijkstra\)本质上的思想是贪心,它只适用于不含负权边的图.
  • 我们把点分成两类,一类是已经确定最短路径的点,称为"白点",另一类是未确定最短路径的点,称为"蓝点"
  • \(dijkstra\)的流程如下\(:\)
  • \(1.\) 初始化\(dis[start] = 0,\)其余节点的\(dis\)值为无穷大.
  • \(2.\) 找一个\(dis\)值最小的蓝点\(x,\)把节点\(x\)变成白点.
  • \(3.\) 遍历\(x\)的所有出边\((x,y,z),\)若\(dis[y] > dis[x] + z,\)则令\(dis[y] = dis[x] + z\)
  • \(4.\) 重复\(2,3\)两步,直到所有点都成为白点\(.\)
  • 时间复杂度为\(O(n^2)\)

\(dijkstra\)为什么是正确的

  • 当所有边长都是非负数的时候,全局最小值不可能再被其他节点更新.所以在第\(2\)步中找出的蓝点\(x\)必然满足\(:dis[x]\)已经是起点到\(x\)的最短路径\(.\)我们不断选择全局最小值进行标记和拓展,最终可以得到起点到每个节点的最短路径的长度

图解

  • (令\(start = 1\))
  • 开始时我们把\(dis[start]\)初始化为\(0\),其余点初始化为\(inf\)

  • 第一轮循环找到\(dis\)值最小的点\(1\),将\(1\)变成白点,对所有与\(1\)相连的蓝点的\(dis\)值进行修改,使得\(dis[2]=2,dis[3]=4,dis[4]=7\)

  • 第二轮循环找到\(dis\)值最小的点\(2\),将\(2\)变成白点,对所有与\(2\)相连的蓝点的\(dis\)值进行修改,使得\(dis[3]=3,dis[5]=4\)

  • 第三轮循环找到\(dis\)值最小的点\(3\),将\(3\)变成白点,对所有与\(2\)相连的蓝点的\(dis\)值进行修改,使得\(dis[4]=4\)

  • 接下来两轮循环分别将\(4,5\)设为白点,算法结束,求出所有点的最短路径
  • 时间复杂度\(O(n^2)\)

为什么\(dijkstra\)不能处理有负权边的情况?

  • 我们来看下面这张图

  • \(2\)到\(3\)的边权为\(-4\),显然从\(1\)到\(3\)的最短路径为\(-2\) \((1->2->3).\)但在循环开始时程序会找到当前\(dis\)值最小的点\(3\),并标记它为白点.
  • 这时的\(dis[3]=1,\)然而\(1\)并不是起点到\(3\)的最短路径.因为\(3\)已经被标为白点,所以\(dis[3]\)不会再被修改了.我们在边权存在负数的情况下得到了错误的答案.

\(dijkstra\)的堆优化?

  • 观察\(dijkstra\)的流程,发现步骤\(2\)可以优化

  • 怎么优化呢?

  • 我会zkw线段树!我会斐波那契堆!

  • 我会堆!

  • 我们可以用堆对\(dis\)数组进行维护,用\(O(\log_{2}n)\)的时间取出堆顶元素并删除,用\(O(\log_{2}n)\)遍历每条边,总复杂度\(O((n+m)\log_{2}n)\)

  • 范例代码:

#include<bits/stdc++.h>

const int MaxN = 100010, MaxM = 500010;

struct edge
{
int to, dis, next;
}; edge e[MaxM];
int head[MaxN], dis[MaxN], cnt;
bool vis[MaxN];
int n, m, s; inline void add_edge( int u, int v, int d )
{
cnt++;
e[cnt].dis = d;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
} struct node
{
int dis;
int pos;
bool operator <( const node &x )const
{
return x.dis < dis;
}
}; std::priority_queue<node> q; inline void dijkstra()
{
dis[s] = 0;
q.push( ( node ){0, s} );
while( !q.empty() )
{
node tmp = q.top();
q.pop();
int x = tmp.pos, d = tmp.dis;
if( vis[x] )
continue;
vis[x] = 1;
for( int i = head[x]; i; i = e[i].next )
{
int y = e[i].to;
if( dis[y] > dis[x] + e[i].dis )
{
dis[y] = dis[x] + e[i].dis;
if( !vis[y] )
{
q.push( ( node ){dis[y], y} );
}
}
}
}
} int main()
{
scanf( "%d%d%d", &n, &m, &s );
for(int i = 1; i <= n; ++i)dis[i] = 0x7fffffff;
for( register int i = 0; i < m; ++i )
{
register int u, v, d;
scanf( "%d%d%d", &u, &v, &d );
add_edge( u, v, d );
}
dijkstra();
for( int i = 1; i <= n; i++ )
printf( "%d ", dis[i] );
return 0;
}

例题

  • 入门模板:P3371
  • 进阶模板:P4779
  • 其余例题请右转洛谷题库,搜索"最短路"

后记

  • 本文部分内容摘自李煜东《算法竞赛进阶指南》和《信息学竞赛一本通》
  • 友情提示:正权图请使用\(dijkstra\)算法,负权图请使用\(SPFA\)算法

A simple dispiction of dijkstra的更多相关文章

  1. tricks - 思维

    编辑 目录 tricks 系列 随机的性质 bitmask 建图 最基本的 黑白染色 Kruskal重构树 并查集维护值域 带根号的数三元环 根号分治 调和级数哈希 多属性哈希 时光倒流 时光反复横跳 ...

  2. [ACM_图论] Domino Effect (POJ1135 Dijkstra算法 SSSP 单源最短路算法 中等 模板)

    Description Did you know that you can use domino bones for other things besides playing Dominoes? Ta ...

  3. POJ 1135 Domino Effect (Dijkstra 最短路)

    Domino Effect Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9335   Accepted: 2325 Des ...

  4. 一个对 Dijkstra 的采访视频

    之前在微博上推荐了一个对 Dijkstra 的采访视频,看了两遍之后觉得实在很好,所以再正式推荐一下.大部分人可能都知道他对图论算法和操作系统的贡献,而其实 Dijkstra 在程序语言上的造诣也很深 ...

  5. 【转】一个对 Dijkstra 的采访视频

    一个对 Dijkstra 的采访视频 (也可以访问 YouTube 或者从源地址下载 MPEG1,300M) 之前在微博上推荐了一个对 Dijkstra 的采访视频,看了两遍之后觉得实在很好,所以再正 ...

  6. 数据结构与算法-图的最短路径Dijkstra

    一  无向图单源最短路径,Dijkstra算法 计算源点a到图中其他节点的最短距离,是一种贪心算法.利用局部最优,求解全局最优解. 设立一个visited访问和dist距离数组,在初始化后每一次收集一 ...

  7. BNU 4356 ——A Simple But Difficult Problem——————【快速幂、模运算】

    A Simple But Difficult Problem Time Limit: 5000ms Memory Limit: 65536KB 64-bit integer IO format: %l ...

  8. BNU 28887——A Simple Tree Problem——————【将多子树转化成线段树+区间更新】

    A Simple Tree Problem Time Limit: 3000ms Memory Limit: 65536KB This problem will be judged on ZJU. O ...

  9. HDU 6166.Senior Pan()-最短路(Dijkstra添加超源点、超汇点)+二进制划分集合 (2017 Multi-University Training Contest - Team 9 1006)

    学长好久之前讲的,本来好久好久之前就要写题解的,一直都没写,懒死_(:з」∠)_ Senior Pan Time Limit: 12000/6000 MS (Java/Others)    Memor ...

随机推荐

  1. Tr/ee AtCoder - 4433 (构造)

    大意: 给定长$n$的字符串$s$, 要求构造一棵树, 满足若第$i$个字符为$1$, 那么可以删一条边, 得到一个大小为$i$的连通块. 若为$0$则表示不存在一条边删去后得到大小为$i$的连通块. ...

  2. .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化

    原文:.NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化 最近我大幅度重构了我一个库的项目结构,使之使用最新的项目文件格式(基于 Microsoft.NET.Sdk)并使 ...

  3. mybatis使用foreach处理List中的Map mybatis-----传入传出多个参数,都是map或list,批量更新

    https://nannan408.iteye.com/blog/2170470 https://blog.csdn.net/xingzhishen/article/details/86424395 ...

  4. Dockerfile构建jar镜像

    dockerDockerfilejar包docker-compose 一.安装docker和compose 二.准备jar包 三.编写配置文件 1. Dockerfile 2. docker-comp ...

  5. python day 20: 线程池与协程,多进程TCP服务器

    目录 python day 20: 线程池与协程 2. 线程 3. 进程 4. 协程:gevent模块,又叫微线程 5. 扩展 6. 自定义线程池 7. 实现多进程TCP服务器 8. 实现多线程TCP ...

  6. Oracle数据库之查询

    一.单表简单查询: 1. select * from scott.emp 2.去重: --去除重复记录 select distinct ssex from java0322; select disti ...

  7. JS-练习题

    1.foo()结果 function foo() { bar.apply(null, arguments); } function bar(){ console.log(arguments); } f ...

  8. MySQL CentOS7 手动安装

    手动安装MySQL的目的: 1.一个服务器上可以同时装多个版本,甚至相同版本MySQL的多个实例,这种需求很常见: 2.一次初始化和配置后,可以快速复制到本服务器或其他服务器,及封装为初始MySQL的 ...

  9. oracle中的存储过程(实例一)

    引子 这是测试环境存在了很久的问题.由于基础配置信息(如:代理人信息)不像生产环境有专人维护,常常会有数据过期,导致无法使用的情况. 而很多配置数据是在外围系统维护(如代理人信息,在销管系统)以往的解 ...

  10. npm 更换阿里淘宝源

    执行命令 npm config set registry https://registry.npm.taobao.org/ 查看是否已经更换成功 npm config get registry