首页 > 其他分享 >Dijkstra

Dijkstra

时间:2024-05-12 21:09:42浏览次数:11  
标签:dist int dijkstra st Dijkstra 最小 短距离

非常经典的单源最短路算法。仅能用于正权图(边权可为 \(0\))
拥有朴素版 \(O(n^2)\)
和堆优化版 \(O((n + m)\log{m})\)
朴素版一般用邻接矩阵存图
而优化版使用邻接表或者链式前向星,我常用链式前向星

中心思想

每次在没用过的点内找一个距离起点最近的点,用这个点对其他点进行松弛操作。
如果把这个点到起点的最短距离称为边的话,即找最短未使用的边,并用这个边对其他所以边进行松弛。

松弛即利用某个点使得某条边的距离变短,如下图:

可以看出,从 \(1\) 到 \(2\) 的距离为 \(5\),但从 \(1\) 到 \(3\),再从\(3\) 到 \(2\) 的总距离仅为 \(3\),因此就可以用 \(3\) 这个点,对 \(1\) 到 \(2\) 之间的最短距离缩小,即松弛。有点时候我喜欢叫扩展

注意当一个点为用过,说明这个点当时已经有最小距离了。

实操步骤

  1. 枚举每个点的 dist,找到最小的未使用的dist
  2. 对这个点进行标记
  3. 利用这个点对其他点进行松弛也就是,dist[j] = max(dist[j], dist[t] + w[j, t])

正确性证明

可以看成,这是贪心的思想,正常的思路,每次都找最小边,那么最后得到的最短距离也应为最小。
反证法
因为是边权非负,所以每次选取的最短边距离dist是单调不减序列。
设一个点为u,当它被使用后,它的dist为最短距离,如果再此之后,还有其他点k能把他扩展更小,因为每次选的dist单调不减,那么dist[k] >= dist[u],而两点间距离w[k, u]最小为0,那么dist[k] + w[k, u] >= dist[u],那么这个点k就无法使得dist[u]更小,那么说明这种情况不可能,从而说明算法是正确的。

从这里也可以看出来,如果上面w[k, u] < 0那么k就有可能吧dist[u]变得更小,而我们是不会再去使用dist[u]去扩展其他点的,也就是说,u之后的一些点无法利用这个dist,变得更小,从而使得我们的算法错误。
例子

可以看出,我们会先用2去把4更新成dist为4。然后才会去用3去更新,这时候,dist[2]会变成-95,但因为2已经使用过了,所以不会再去使用它了,于是dist[4]无法更新,算法错误。

代码

中心思想就是这样,代码也显而易见

朴素dijkstra (邻接表)

dijkstra 正确性来自于贪心 也就是 \(st\) 数组内的数(dist) 必须逐渐变大这样才能保证后面的数更新的时候,当前的第三边dist[t]都是最小值
dist[x]表示xstart的最短距离

int dijkstra()
{
    dist[start] = 0;
    int k = n;
    while (k -- ) // 运行n - 1 次就行 n次一样不错就是多算一遍 可改成 --k
    {
        int t = -1;
        for (int i = 1; i <= n; i ++ )
            if (!st[i] && (t == -1 || dist[t] > dist[i]))
                t = i;
                
        st[t] = true;
        
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            dist[j] = min(dist[j], dist[t] + w[i]);
        }
    }
    
    return dist[ened];
}

朴素dijkstra 邻接矩阵

看代码或者理论应该就能看出来,是 \(O(n^2)\) 的

int dijkstra()
{
    dist[start] = 0;
    int k = n;
    
    while (k -- )
    {
        int t = -1;
        for (int i = 1; i <= n; i ++ )
            if (!st[i] && (t == -1 || dist[t] > dist[i]))
                t = i;
                
        st[t] = true;
        
        for (int i = 1; i <= n; i ++ )
            dist[i] = min(dist[i], dist[t] + g[t][i]);
    }
    
    return dist[ened];
}

堆优化dijkstra

关于堆优化 Dijkstra,就是优化了找最小 dist 点的过程,使用了小根堆进行排序,把原来 \(O(n)\) 的枚举换成 \(O(logm)\) 的排序,从而优化了时间复杂度,也因此堆优化Dijkstra有个\(O(logm)\) 而遍历所有点和边是 \(O(n + m)\) 的,加起来就是 \(O((n+m)logm)\)
小根堆排序,一般用pair存,因为要用 dist 来排序,还要记录这个点的下标,并且pair自带,第一键值优先的排序性质,所以使用。

#define x first
#define y second

typedef pair<int, int> PII;

int dijkstra()
{
    priority_queue<PII, vector<PII>, greater<PII>> q;
    q.push({0, S});
    dist[S] = 0;
    while (q.size())
    {
        auto t = q.top();
        q.pop();
        int ver = t.second, distance = t.first;
        if (st[ver]) continue;
        st[ver] = true;      // 标记已经用这个点更新过了 (此点目前最小)
        
        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                q.push({dist[j], j});
            }
        }
    }
    return dist[T];
}

扩展

虽然是最短路算法,但是算单源最长路也是可以的,证明和上面类似

标签:dist,int,dijkstra,st,Dijkstra,最小,短距离
From: https://www.cnblogs.com/blind5883/p/18188171

相关文章

  • 洛谷P1576最小花费(逆Dijkstra算法)
    背景:说实话,这题有点考建模思想,及对Dijkstra算法的理解思路:因为转账间有手续费,即有一定损失比例,故边权均小于1(比例来说),而边的路权值非和,而是积,故边权相当于负(因为每次乘会使dis[i]变小)而题目刚好求最大路,而边权又等价于全为负,不刚好是Dijkstra的逆运用吗?原理:等......
  • 力扣周赛394之别样DP + 别样Dijkstra
    别样DP题目链接https://leetcode.cn/problems/minimum-number-of-operations-to-satisfy-conditions/description/题目大意题目思路需要考虑m列每一列填什么的情况,因为最终每一列都是一样的考虑暴力,每一列都可以变成0-9有\(10^m\)次种情况,这必然是不可行的我们从前往......
  • Dijkstra算法
    单源最短路算法,不能处理负环,朴素版时间复杂度\(O(n^2)\),堆优化版时间复杂度\(O(nlogn)\)。Dijkstra算法的流程是:将所有的节点分为A、B的两个集合,一开始A集合中只有起点,其他的节点在B集合。定义B中的节点与A的距离:若邻接A中的结点,则距离为边权;反之距离无穷大。1.找到与A距离最小......
  • 最短路算法(Dijkstra + SPFA + Floyd)
    最短路算法(Dijkstra+SPFA+Floyd)Dijkstra算法1.算法基本介绍Dijkstra算法通常是求解单源最短路中最快的算法,但它无法处理存在负权边的情况(原因在正确性证明中)。Dijkstra本质上是一种贪心算法,通过不断调整每个点的“当前距离”最终得到最优结果,其实后面要讲到的几种算法也大......
  • 【科研相关知识】Dijkstra算法
    Dijkstra算法相关背景知识历史来源exampleDijkstra算法相关背景知识Dijkstra算法是解决图论中的最短路径问题而最短路径问题是在图中找到两个节点之间的最短路径在导航中确定路线最短,在网络中路由器使用Dijkstra算法确定最短路径和转发端口。最短路径算法有Dijkstra算......
  • 【学习笔记】dijkstra
    一、dijkstra1.定义&作用很简单。一个单源最短路算法。2.思想&实现我觉得sm的图已经够了。二、堆优化dijkstra1.先来认识一下proirity_queue2.如何使用proirity_queue对dijkstra优化?每一步,我们都需要找到\(d\)值最小的节点(即\(......
  • FFmpeg 7.0 “Dijkstra” 发布
    FFmpeg7.0“Dijkstra”发布来源:OSCHINA编辑: 白开水不加糖2024-04-0710:11:00 2国产数据库圈,为啥那么多水货?”FFmpeg7.0“Dijkstra”现已发布。此版本以荷兰计算机科学家EdsgerW.Dijkstra的名字命名,一些值得注意的变化包括原生VVC解码器(目前处于......
  • DIjkstra进阶模板 路径记录 按权重(结点数最小等)记录
    structDIJ{usingi64=longlong;usingPII=pair<i64,i64>;vector<i64>dis,path,node;vector<vector<array<int,3>>>G;intn;DIJ(){}DIJ(intn):n(n){node.resize(n+1,1);......
  • Pintia 天梯地图 dijkstra进阶
    7-14天梯地图-SMU2024spring天梯赛3(补题)(pintia.cn)dijkstra进阶做法,包含路径记录,以及按权重统计路径条件等;不过最开始我一直将优先队列开的最大堆,但是一直过不了自己的例子,后来改成最小堆并且路径值改成负数存进去就对了,再后来我发现改成最大堆也可以,不过要把......
  • Dijkstra邻接矩阵板子
    constintN=510;//节点个数intn;intg[N][N];//图intdist[N];//距离boolst[N];//判断点是否访问过intdijkstra(ints)//s表示起点,求s到任意点的最短距离{memset(dist,0x3f,sizeofdist);dist[s]=0;for(inti=0;i<n;i++){......