1 条题解

  • 0
    @ 2025-8-24 22:03:33

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar little_sun
    chsyao & xuezhe就是我们的红太阳!

    搬运于2025-08-24 22:03:33,当前版本为作者最后更新于2018-07-24 18:55:25,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    前言

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

    什么是dijkstradijkstra?

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

    dijkstradijkstra的原理/流程?

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

    dijkstradijkstra为什么是正确的

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

    图解

    • (令start=1start = 1)
    • 开始时我们把dis[start]dis[start]初始化为00,其余点初始化为infinf 初始化
    • 第一轮循环找到disdis值最小的点11,将11变成白点,对所有与11相连的蓝点的disdis值进行修改,使得dis[2]=2,dis[3]=4,dis[4]=7dis[2]=2,dis[3]=4,dis[4]=7 1
    • 第二轮循环找到disdis值最小的点22,将22变成白点,对所有与22相连的蓝点的disdis值进行修改,使得dis[3]=3,dis[5]=4dis[3]=3,dis[5]=4 2
    • 第三轮循环找到disdis值最小的点33,将33变成白点,对所有与22相连的蓝点的disdis值进行修改,使得dis[4]=4dis[4]=4 3
    • 接下来两轮循环分别将4,54,5设为白点,算法结束,求出所有点的最短路径
    • 时间复杂度O(n2)O(n^2)

    为什么dijkstradijkstra不能处理有负权边的情况?

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

    dijkstradijkstra的堆优化?

    • 观察dijkstradijkstra的流程,发现步骤22可以优化

    • 怎么优化呢?

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

    • 我会堆!

    • 我们可以用堆对disdis数组进行维护,用O(log2n)O(\log_{2}n)的时间取出堆顶元素并删除,用O(log2n)O(\log_{2}n)遍历每条边,总复杂度O((n+m)log2n)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
    • 其余例题请右转洛谷题库,搜索"最短路"

    后记

    • 本文部分内容摘自李煜东《算法竞赛进阶指南》和《信息学竞赛一本通》
    • 友情提示:正权图请使用dijkstradijkstra算法,负权图请使用SPFASPFA算法
    • 感谢洛谷各位管理员提供的平台

    博客传送门

    个人博客

    • 1

    信息

    ID
    3777
    时间
    1000ms
    内存
    125MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者