【算法笔记】单源最短路Dijkstra
何为Dijkstra
单源最短路,即从一个点出发,到其他所有点的最短距离,起点可以是任意一点
Dijkstra的本质是贪心
过程

在这张图中,如果我们要\(1\)号点位起点\(st\),求最短路的过程大概是这样的
定义一个d数组,其中\(d[i]\)代表从起点\(st\)到\(i\)的最短距离,首先认为\(st\)到所有点的距离都是\(inf\),也就是我们要做的初始化
初始化之后就变成了这样

从当前点出发,找所有和它邻接的点,看能否用这条边更新最短路的长度
先从\(1\)号点出发更新\(2\)号点,我们发现\(0+1<inf\),于是我们欣然接受这个方案,将\(2\)号点的距离更新成\(1\),也就是这样

同理我们也可以更新\(3\)号点的距离
更新完\(3\)号点后,\(1\)号点的所有邻接点全部更新完毕,也可以说\(1\)号点的使命结束,可以删掉了
此时这张图就变成了这样

然后我们再选择一个和原点距离最近的点(也就是\(2\)号点)进行更新
和上面同理,我们可以对\(3\)号点和\(4\)号点进行更新
我们并不需要关心具体需要怎样走,我们只关心从\(2\)号点到\(4\)号点时\(1+2<inf\)以及\(2\)号点到\(3\)号点时\(1+2<4\),新的距离比原来的距离短,我们就进行更新
此时\(2\)号点使命结束,删除
我们注意到\(3\)和\(4\)到原点的距离相同,此时随便选一个进行拓展即可
拓展\(5\)号点同理
拓展完的图就变成了这样

此时我们再看这句话,相信你就能够理解
Dijkstra的本质是贪心
其中,贪心体现为每次都找最近点进行拓展,可以保证每次开始拓展时,这个点到原点的距离一定是最小的
此处可以进行反证:
如果这个点的距离不是最小的,就一定能找到另一个点离\(st\)更近来进行拓展
简单总结一下
Dijkstra的过程大概可以分为这样几步:
-
初始化\(d\)数组
-
找出距离最小的点\(u\)
-
遍历\(u\)的所有出边,并进行更新
还没懂的话可以看看这里
朴素版代码
ll d[N];//d[i]为原点到点i的最短距离
void dijkstra(int x)//x是原点
{
memset(d, 0x3f, sizeof(ll) * (n + 1));
d[x] = 0;//自己到自己的距离为0
bitset<N> vis;//true表示已经拓展过
for(int i = 1; i <= n - 1; ++ i)
{
//找出最小点(距离源点最近的点)
int u = 1;
for(int j = 1; j <= n; ++ j)
if(vis[u] || (!vis[j] && d[j] < d[u]))u = j;
vis[u] = true;//表示u已经拓展过
//此时d[u]已为最优
for(const auto &[v, w] : g[u])
if(!vis[v] && d[v] > d[u] + w)d[v] = d[u] + w;
}
}
上面这段代码的时间复杂度为\(O(n^2)\),能够计算的\(n\)的范围大约是\(10^4\)以内,在大多数情况下并不能过题,所以我们就需要对一些步骤进行优化
很明显,初始化和遍历所有出边这两步并没有什么方案可以优化,我们便把目光放在第二步
优化
插入元素,查询最小值,删除最小值
这几步操作很容易让我们想到堆,也就是优先队列
我们可以用距离为第一关键字进行比较,并存入一个小根堆,这样我们查询最小值时就可以优化到\(log\)级别
堆优化代码
struct node
{
ll x, w;//x表示出点 w表示权值
bool operator < (const node &v)const//按照w小的优先
{
return w == v.w ? x < v.x : w > v.w;
}
};
vector<node> g[N];
ll d[N];
int n, m;
void dijkstra(int x)
{
memset(d, 0x3f, sizeof(ll) * (n + 1));
d[x] = 0;//初始化起点
bitset<N> vis;//表示已经拓展过
priority_queue<node> pq;//定义了一个大根堆,但是重载运算符时进行了反转,也就是堆顶元素的距离最小
pq.push({x, d[x]});//将起点作为拓展点
while(!pq.empty())
{
int x = pq.top().x;pq.pop();//取出距离最小的元素
if(vis[x])continue;//跳过冗余的拓展点
vis[x] = true;//取出的x点已经得到了最短距离d[x]
for(const auto &[v, w] : g[x])
{
if(!vis[v] && d[v] > d[x] + w)//如果vis[y],说明y已经得到了最短距离,无需更新
{
d[v] = d[x] + w;
pq.push({v, d[v]});
}
}
}
}

浙公网安备 33010602011771号