Dijkstra
Dijkstra
前言
总感觉最近几天自己摸鱼摸得有点厉害,整个人都不再状态。
今天好不容易下定决心一定要把\(Dijkstra\)整理出来,因为这个工作自己已经鸽了好几天了,实在无法忍受他继续呆在我的脑子里面碍事,于是今天决定对他下手。
好嘛~
爷来了~
蛋是……
当然了,有一个小小的问题:
就是,我,其实还是并不太会写\(DJ\)的代码,
但是在经历了为无数题解的洗礼之后,我已经完全明白,\(DJ\)的是思路了。
什么是\(Djikstra\)
借用洛谷中某一篇题解的介绍:
\(Dijkstra\)是一种单源最短路径算法,时间复杂度的上限为\(O\)(\(n^2\))(朴素),在实际应用中较为稳定;加上堆优化之后更是具有\(O\)((\(m+n\))\(log_2n\))的时间复杂度,在稠密图中有不俗的表现。
啊吧啊吧……在线吃瓜。
\(Djikstra\)的使用说明
产品名称:\(Djikstra\)
使用限制:仅限于无负边权的图
原因:你见过那个说明书告诉你原因?所以……没有
使用方法:口服
啊不是不是,其实呢……\(Dijkstra\)的本质上的思想就是贪心(毕竟最短路问题是完全可以贪心的)
使用\(Dijkstra\)其实非常简单,其实就是初始化一个点。
使他的\(dis\)[\(start\)]=0,然后把其他所有结点的\(dis\)[\(i\)]设为无穷大。
然后从\(start\)开始向所能到达的点进行遍历。
寻找所需要消耗的边权最少的一条边,然后更新所到达的点的点权为\(dis[start]+w[i]\),\(w[i]\)表示的是边权。
然后从刚才更新的那个点开始重复操作。
但是在后续的操作之中需要注意比较更新之后的点的权值和点原来的权值,如果大于就不能更新。
至于为什么不再上面写呢……
一是因为我忘了,
而是因为上面已经设成了\(MAX\)
显然不可能出现更大的权了,
必然更新。
\(Dijkstra\)为什么是正确的
依然借用题目。
其实意思就是我们要证明\(Dijkstra\)的正确性。
其实很简单,甚至有些显而易见。
当所有的边权都是正的的时候,无论怎么相加,必然你能够更新出最小的点权,从而实现从某一个点出发到达其余所有的点的单源最短路径。
没有图解
\(Dijkstra\)的堆优化
众所周知,在\(Dijkstra\)的每一步操作之中,都需要找到当前所在点所连通的边的边权最小的一条。
所以,
自然而然地,
想到了堆!
我们可以使用堆对\(dis\)数组进行维护,用\(O(log_2n)\)的时间取出堆顶元素并删除,用\(O(log_2n)\)的时间遍历每条边,总复杂度为\(O((m+n)log_2n)\)
范例代码
本来是没有的,但是怕有人打我,于是从洛谷\(copy\)了一份
致敬原作者:
little_sun
感谢代码:
#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;
}
此致,
敬礼!