dijkstra 详解

dijkstra 详解

dijkstra 详解

little_sun

·

2018-07-24 18:55:25

·

题解

前言

什么是dijkstra?

dijkstra的原理/流程?

我们把点分成两类,一类是已经确定最短路径的点,称为"白点",另一类是未确定最短路径的点,称为"蓝点"

dijkstra$的流程如下$:

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不能处理有负权边的情况?

我们来看下面这张图

这时的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

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 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算法

感谢洛谷各位管理员提供的平台

博客传送门

个人博客

你可能也喜欢

手机转账
beat365中文官网

手机转账

📅 08-11 👀 2023
来了!!小鱼一键重装系统最全面科普!!!
beat365中文官网

来了!!小鱼一键重装系统最全面科普!!!

📅 08-05 👀 4131
哪些品牌有粉色汽车的款式?
beat365中文官网

哪些品牌有粉色汽车的款式?

📅 07-15 👀 8450
暑假有哪些趣味活动?7月科普月历来啦!
beat365中文官网

暑假有哪些趣味活动?7月科普月历来啦!

📅 07-30 👀 1236
手机如何正常看html文件
36500365体育在线投注

手机如何正常看html文件

📅 07-13 👀 2357
哪些品牌有粉色汽车的款式?
beat365中文官网

哪些品牌有粉色汽车的款式?

📅 07-15 👀 8450