AcWing 849 Dijkstra求最短路 I

题目描述:

给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为正值。

请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出-1。

输入格式

第一行包含整数n和m。

接下来m行每行包含三个整数x,y,z,表示点x和点y之间存在一条有向边,边长为z。

输出格式

输出一个整数,表示1号点到n号点的最短距离。

如果路径不存在,则输出-1。

数据范围

1≤n≤500,
1≤m≤10^5,
图中涉及边长均不超过10000。

输入样例:

3 3
1 2 2
2 3 1
1 3 4

输出样例:

3

分析:

本题涉及的图为稠密图,采用邻接矩阵存储,无负权边,采用朴素的dijkstra算法时间复杂度为O(n^2)。

简单描述下dijkstra算法的过程:从点集的角度去理解该算法。无负权边,求从1号点到n号点的最短距离。初始点集只包含1号点,我们需要添加一些点直至将n号点也添加进点集为止。

随便弄张图来讲解下算法的流程:初始点集为{A},d[A]=0,其它顶点的初始距离设为不可达INF,A能够到达B和C,则更新下d[B] = 1,d[C] = 5,然后找下除了点集内的点其它点距离的最小值,d[B]最小,为1,所以将B加入点集,点集为{A,B},然后尝试用B来更新相邻节点,更新d[D] = d[B] + 2 = 3,d[E] = d[B] + 1 = 2。再寻找点集中的点可到扩展到的最近的点,发现是E点,遂将E加入点集,更新C点的最短距离为d[C]=4,D点未能更新最短距离,d[G]=4。继续寻找点集{A,B,E}以外可到达的最近的点为D点,加入集合,看看能否用D点更新附近的点,d[F] = 5,继续寻找最近的点为C点,加入点集,现在的点集为{A,B,E,D,C},通过C点没有可更新的距离,将最近的点G加入集合,发现还是不能更新F点,最后将F加入点集,得到{A,B,E,D,C,G,F}。从A到F的最短路径为d[F]=5。

可以发现的是,当一个点被加入点集,起点到该点的最短距离就必然已经确定下来了。初始点集由A扩展到B,显然B是离A最近的点,再扩展到E,说明点集E离集合中的点已经是最近的了,假设点集中点经过某点x再到达E可以更新E的最短距离,由于没有负权边,点集中点到x的距离必然小于到E的距离,而E加入点集的条件恰好是到点集的距离最近,所以不存在这样的点x。以此类推,可以证明当终点被加入到点集中,其最短距离也就确定了。

整理下dijkstra算法的过程:每次遍历下所有的边,找到点集外可加入点集的距离最小的点,加入到点集,再尝试更新下新加入的点附近的点的距离。每次执行下找最短边的操作时间复杂度是O(n),最坏情况下终点在最后才会被加入到点集,所以过程会被重复n次,故朴素的dijkstra算法的时间复杂度为O(n^2)。

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 510;
int n,m;
int g[maxn][maxn],d[maxn];
bool vis[maxn];
int dijkstra(){
    memset(d,0x3f,sizeof d);//初始距离不可达
    d[1] = 0;
    for(int i = 0;i < n;i++){//遍历n次
        int t = -1;
        for(int j = 1;j <= n;j++){
            if(!vis[j] && (t == -1 || d[t] > d[j])) t = j;//找点集外初始最短边
        }
        vis[t] = true;//加入点集
        for(int j = 1;j <= n;j++){
            if(!vis[j] && d[t] + g[t][j] < d[j])    d[j] = d[t] + g[t][j];//松弛操作
        }
    }
    if(d[n] == 0x3f3f3f3f)  return -1;//终点不可达
    return d[n];
}
int main(){
    scanf("%d%d",&n,&m);
    int a,b,c;
    memset(g,0x3f,sizeof g);
    while(m--){
        scanf("%d%d%d",&a,&b,&c);
        g[a][b] = min(g[a][b],c);
    }
    cout<<dijkstra()<<endl;
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值