洛谷P4779 【模板】单源最短路径(标准版)(dijkstra+优先队列-堆优化:时间复杂度o((m+n)logn))

题目背景

2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。

然后呢?

100 \rightarrow 60100→60;

Ag \rightarrow CuAg→Cu;

最终,他因此没能与理想的大学达成契约。

小 F 衷心祝愿大家不再重蹈覆辙。

题目描述

给定一个 NN 个点,MM 条有向边的带非负权图,请你计算从 SS 出发,到每个点的距离。

数据保证你能从 SS 出发到任意点。

输入格式

第一行为三个正整数 N, M, SN,M,S。 第二行起 MM 行,每行三个非负整数 u_i, v_i, w_iui​,vi​,wi​,表示从 u_iui​ 到 v_ivi​ 有一条权值为 w_iwi​ 的边。

输出格式

输出一行 NN 个空格分隔的非负整数,表示 SS 到每个点的距离。

输入输出样例

输入 #1复制

4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4

输出 #1复制

0 2 4 3

说明/提示

样例解释请参考 数据随机的模板题

1 \leq N \leq 1000001≤N≤100000;

1 \leq M \leq 2000001≤M≤200000;

S = 1S=1;

1 \leq u_i, v_i\leq N1≤ui​,vi​≤N;

0 \leq w_i \leq 10 ^ 90≤wi​≤109,

0 \leq \sum w_i \leq 10 ^ 90≤∑wi​≤109。

本题数据可能会持续更新,但不会重测,望周知。

2018.09.04 数据更新 from @zzq

迪杰斯特拉朴素的模板时间复杂度是o(n^2),这里可以用优先队列模拟堆来优化一下,主要是在每次取当前到源点最近的点时可以用一个优先队列来取其队头元素即可,就不用再遍历n个点了,只需要用堆的logn的时间修改调整堆,每次o(1)的时间取就行了,时间复杂度就降到了o((n+m)logn)。

完整代码:

#include <bits/stdc++.h>
#define int long long
const int maxn=5e+5,inf=0x3f3f3f3f;
using namespace std;
typedef struct Edge
{
    int next,to,dis;//to为终点编号,next是同起点的上一条边的编号,dis为该边的权值
}edg;
edg e[maxn];//链式前向星建图
int head[maxn],cnt,dis[maxn],vis[maxn],n,m,s;//dis[i]表示编号为i的点到源点的最短距离,vis[i]表示i是否在已找出的最短距离的点的集合中
//head[i]表示以i为起点的当前边的编号
struct node
{
    int dis,u;//dis为点到源点的距离,u为点的编号
    bool operator <(const node &x)const
    {
        return x.dis<dis;//定义优先队列(即堆)的顺序为按点到源点的距离由小到大
    }
};
priority_queue<node>q;

void init()//初始化
{
    for(int i=1;i<=n;i++)
        dis[i]=inf;//当一开始每个点到源点的最短距离都为正无穷
}

void AddEdge(int u,int v,int w)
{
    e[cnt].to=v;
    e[cnt].dis=w;
    e[cnt].next=head[u];
    head[u]=cnt++;
}

void dijkstra()
{
    dis[s]=0;
    q.push((node){0,s});//先让源点入队
    while(!q.empty())
    {
        node x=q.top();
        q.pop();
        int u=x.u;//u为当前未加入已找到最短距离的集合的所有点中的到源点距离的最小的点的编号
        if(vis[u])
            continue;//一个点可能先后多次被加入集合,如果已经标记过,就可以直接继续找下一个了
        vis[u]=1;
        for(int i=head[u];~i;i=e[i].next)//链式前向星遍历以u为起点的边
        {
            int v=e[i].to;//v即为与u同一条边直接连着的点
            if(dis[u]+e[i].dis<dis[v])
            {
                dis[v]=dis[u]+e[i].dis;//如果以当前找到的到源点距离最小的点u为中转点的到源点的距离比之前的距离小就更新为以当前找到的到源点距离最小的点u为中转点的到源点的距离
                if(!vis[v])
                    q.push((node){dis[v],v});//如果v不在已找到最短距离的点的集合中,就将v加入集合
            }
        }
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    memset(head,-1,sizeof(head));
    cin>>n>>m>>s;
    init();
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        cin>>u>>v>>w;
        AddEdge(u,v,w);
    }
    dijkstra();
    for(int i=1;i<=n;i++)
        cout<<dis[i]<<" ";
    return 0;
}
发布了89 篇原创文章 · 获赞 5 · 访问量 6686

猜你喜欢

转载自blog.csdn.net/Mr_Kingk/article/details/100176404
今日推荐