Dijkstra 算法(2)
Dijkstra 算法(2)
1.例题
题目描述
给定一个 \(n\) 个点 \(m\) 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。
请你求出 \(1\) 号点到 \(n\) 号点的最短距离,如果无法从 \(1\) 号点走到 \(n\) 号点,则输出 −1
。
输入格式
第一行包含整数 \(n\) 和 \(m\)。
接下来 \(m\) 行每行包含三个整数 \(x,y,z\),表示存在一条从点 \(x\) 到点 \(y\) 的有向边,边长为 \(z\)。
输出格式
输出一个整数,表示 \(1\) 号点到 \(n\) 号点的最短距离。
如果路径不存在,则输出 -1
。
数据范围
\(1≤n,m≤1.5×105,\)
图中涉及边长均不小于 \(0\),且不超过 \(10000\)。
数据保证:如果最短路存在,则最短路的长度不超过 \(109\)。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
注:本题来源于AcWing题库第850题
2.思路
没学过 dijkstra 算法的请到这里去。
首先,我们先来观察一下题目,\(1≤n,m≤1.5×105\),
很明显,dijkstra 算法 \(O(n^2)\) 的时间复杂度是会TLE的。
这时我们就需要进行优化了。
在寻找一个未标记且最近的点(下面我们把这个点叫做 \(t\) 点)时,朴素版 dijkstra 是需要套两重循环来找的,
那我们要在众多数中找一个最小值,能不能用堆来优化呢?
堆排序的时间复杂度为 \(O(mlogn)\),也正好符合我们的要求。
所以,我们便可以在每次用 \(t\) 点来修改 \(j\) 点的最短路时,把 \(j\) 点加到堆里去,进行堆排序操作。
这就是优化!
3.代码
本人用的是优先队列,用手写太麻烦了,我相信没多少人会故意去把代码往难里写。
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=150005;
int n,m;
int w[N],h[N],e[N],ne[N],idx;
int dist[N];
bool st[N];
struct node{
int first,second;//代表距离和编号
bool operator < (const node &x) const{//重载运算符
return first>x.first;
}
};
void add(int a,int b,int c){//用邻接表存储的加边操作
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
int dijkstra(){
memset(dist,0x3f,sizeof(dist));
dist[0]=1;
priority_queue<node> heap;
node a={0,1};//先用1号点初始一下距离
heap.push(a);
while(heap.size()){//判断堆不空也就是看点没有遍历完
node t=heap.top();
heap.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]){//遍历所有t的能到达的点
int j=e[i];//遍历到的这个点到t点的距离
if(dist[j]>distance+w[i]){
dist[j]=distance+w[i];
node p={dist[j],j};//如果老距离大于新距离就更换并入堆
heap.push(p);
}
}
}
if(dist[n]==0x3f3f3f3f) return 0x3f3f3f3f;
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++){
int a,b,w;
cin>>a>>b>>w;
add(a,b,w);
}
if(dijkstra()==0x3f3f3f3f) cout<<"-1";
else cout<<dist[n];
return 0;
}
当然,如果不想写重载运算符也可以去补齐小根堆的两个参数。
完~
如果觉得还不错,就点个赞吧,您的支持就是我最大的动力。