D02【模板】最短路 Dijkstra 算法 P4779 单源最短路径

D02 最短路 Dijkstra 算法——信息学竞赛算法_哔哩哔哩_bilibili

 

最短路 - OI Wiki

Dijkstra 算法是一种基于贪心思想的求解 非负权图单源最短路径的算法.

将结点分成两个集合:已确定最短路长度的点集(记为 𝑆 集合)的和未确定最短路长度的点集(记为 𝑇 集合).一开始所有的点都属于 𝑇 集合.

初始化 𝑑𝑖𝑠(𝑠) =0,其他点的 𝑑𝑖𝑠 均为 +∞

然后重复这些操作:

  1. 从 𝑇 集合中,选取一个最短路长度最小的结点(贪心思想),移到 𝑆 集合中.
  2. 对那些刚刚被加入 𝑆 集合的结点的所有出边执行松弛操作.

直到 𝑇 集合为空,算法结束.

时间复杂度

可以使用优先队列维护,无法执行 删除点 操作,可以通过每次松弛时重新插入该结点,且弹出时检查该结点是否已被松弛过,若是则跳过,复杂度 𝑂(𝑚log⁡𝑛)

在稀疏图中,𝑚 =𝑂(𝑛),堆优化的 Dijkstra 算法具有较大的效率优势;而在稠密图中,𝑚 =𝑂(𝑛2),这时候使用朴素实现更优.

 

P4779 【模板】单源最短路径(标准版) - 洛谷

//堆优化 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]);
}

 

P3371 【模板】单源最短路径(弱化版) - 洛谷

//朴素 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]);
}

 

posted @ 2022-05-28 13:08  董晓  阅读(4431)  评论(0)    收藏  举报