D02【模板】最短路 Dijkstra 算法 P4779 单源最短路径
D02 最短路 Dijkstra 算法——信息学竞赛算法_哔哩哔哩_bilibili
Dijkstra 算法是一种基于贪心思想的求解 非负权图 上单源最短路径的算法.
将结点分成两个集合:已确定最短路长度的点集(记为 𝑆
集合)的和未确定最短路长度的点集(记为 𝑇
集合).一开始所有的点都属于 𝑇
集合.
初始化 𝑑𝑖𝑠(𝑠) =0
,其他点的 𝑑𝑖𝑠
均为 +∞
.
然后重复这些操作:
- 从 𝑇
集合中,选取一个最短路长度最小的结点(贪心思想),移到 𝑆
集合中. - 对那些刚刚被加入 𝑆
集合的结点的所有出边执行松弛操作.
直到 𝑇
集合为空,算法结束.
时间复杂度
可以使用优先队列维护,无法执行 删除点 操作,可以通过每次松弛时重新插入该结点,且弹出时检查该结点是否已被松弛过,若是则跳过,复杂度 𝑂(𝑚log𝑛)
.
在稀疏图中,𝑚 =𝑂(𝑛)
,堆优化的 Dijkstra 算法具有较大的效率优势;而在稠密图中,𝑚 =𝑂(𝑛2)
,这时候使用朴素实现更优.
//堆优化 Dijkstra O(mlogn) #include<bits/stdc++.h> #define pii pair<int,int> using namespace std; const int N=100010; vector<pii> e[N]; int n,m,s; int d[N],vis[N]; void dijkstra(){ memset(d,0x3f,sizeof d); d[s]=0; priority_queue<pii,vector<pii>,greater<pii> > q; //小根堆 q.push({0,s}); while(q.size()){ auto u=q.top().second; q.pop(); if(vis[u])continue; vis[u]=1; //不是第1次出队就跳过,是就标记,然后扩展 for(auto [v,w]:e[u]){ if(d[v]>d[u]+w){ d[v]=d[u]+w; q.push({d[v],v}); } } } } int main(){ cin>>n>>m>>s; for(int i=0,a,b,c;i<m;i++){ scanf("%d%d%d",&a,&b,&c); e[a].push_back({b,c}); } dijkstra(); for(int i=1;i<=n;i++) printf("%d ",d[i]); }
//堆优化 Dijkstra O(mlogn) #include<bits/stdc++.h> #define pii pair<int,int> using namespace std; const int N=100010; vector<pii> e[N]; int n,m,s; int d[N]; void dijkstra(){ memset(d,0x3f,sizeof d); d[s]=0; priority_queue<pii,vector<pii>,greater<pii>> q; //小根堆 q.push({0,s}); while(q.size()){ auto [dd,u]=q.top(); q.pop(); if(dd!=d[u]) continue; //不是第1次出队就跳过。这种判重不安全,有些拆点的题目会出错! for(auto [v,w]:e[u]){ if(d[v]>d[u]+w){ d[v]=d[u]+w; q.push({d[v],v}); } } } } int main(){ cin>>n>>m>>s; for(int i=0,a,b,c;i<m;i++){ scanf("%d%d%d",&a,&b,&c); e[a].push_back({b,c}); } dijkstra(); for(int i=1;i<=n;i++) printf("%d ",d[i]); }
//set优化 Dijkstra O(mlogn) #include<bits/stdc++.h> #define pii pair<int,int> using namespace std; const int N=100010; vector<pii> e[N]; int n,m,s; int d[N]; void dijkstra(){ memset(d,0x3f,sizeof d); d[s]=0; set<pii > q; q.insert({0,s}); while(!q.empty()){ int u=q.begin()->second; q.erase(*q.begin()); for(auto [v,w]:e[u]){ if(d[v]>d[u]+w){ q.erase({d[v],v}); d[v]=d[u]+w; q.insert({d[v],v}); } } } } int main(){ cin>>n>>m>>s; for(int i=0,a,b,c;i<m;i++){ scanf("%d%d%d",&a,&b,&c); e[a].push_back({b,c}); } dijkstra(); for(int i=1;i<=n;i++) printf("%d ",d[i]); }
//朴素 Dijkstra O(n^2) #include<bits/stdc++.h> using namespace std; const int N=100010,inf=(1<<31)-1; int n,m,s,a,b,c; vector<pair<int,int>> e[N]; int d[N],vis[N]; void dijkstra(){ for(int i=0;i<=n;i++) d[i]=inf; d[s]=0; for(int i=1;i<n;i++){ //枚举次数 int u=0; for(int j=1;j<=n;j++) //枚举点 if(!vis[j]&&d[j]<d[u]) u=j; vis[u]=1; //选u for(auto [v,w]:e[u]) //枚举邻点 if(d[v]>d[u]+w) //松弛 d[v]=d[u]+w; } } int main(){ cin>>n>>m>>s; for(int i=0; i<m; i++){ cin>>a>>b>>c; e[a].push_back({b,c}); } dijkstra(); for(int i=1;i<=n;i++) printf("%d ",d[i]); }
浙公网安备 33010602011771号