单源最短路-dijkstra算法及其优化

dijkstra算法及其优化

题目描述:给定一个 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

分析:鉴于n的数据规模比较小,可用朴素迪杰斯特拉算法(适用于稠密图)

算法时间复杂度O(n^2)

code:朴素dijkstra算法

 #include<cstdio>
 #include<cstring>
 #include<iostream>
 #include<algorithm>
 using namespace std;
 
 const int N = 510;
 int g[N][N];
 int dist[N];
 bool st[N];
 int n,m;
 
 int Dijkstra(){
     memset(dist,0x3f,sizeof dist);
     dist[1] = 0;
     
     //迭代n次,每次找到一个点,用它来更新它的可达点到起点的距离。
     for(int i=0;i<n;i++){
         int t = -1;
         //总是选出当前最短路还没有确定的点中到起点最近的点,并用它来更新它的可达点到起点的距离。(这是迪杰斯特拉算法的核心思想--贪心)
         //下面是找到当前没有确定最短路的点里到起点距离最短的点。
         for(int j=1;j<=n;j++){
             if(!st[j]&&(t==-1||dist[j]<dist[t]))
                 t = j;
        }
         st[t] = true;
         //更新可达点
         for(int j=1;j<=n;j++) dist[j] = min(dist[j],dist[t] + g[t][j]);
    }
     
     if(dist[n]==0x3f3f3f3f) return -1;
     return dist[n];
 }
 int main(){
     scanf("%d%d",&n,&m);
     memset(g,0x3f,sizeof g);
     while(m--){
         int x,y,z;
         scanf("%d%d%d",&x,&y,&z);
         g[x][y] = min(g[x][y],z);
    }
     
     int t = Dijkstra();
     printf("%d\n",t);
     return 0;
 }

分析:同一题,如果n的数据规模增大至10^5

那么,对于这种稀疏图,可以采用堆优化策略的dijkstra算法来求解

代码如下:

 //堆优化版dijkstra算法。
 
 #include<queue>
 #include<cstdio>
 #include<cstring>
 #include<iostream>
 #include<algorithm>
 using namespace std;
 
 const int N = 2e5 + 10;
 typedef pair<int,int> PII;//第一分量存的是距离,第二分量存的是点编号
 int h[N],e[N],ne[N],w[N],idx;//w为边的权值 ,用邻接表来存储稀疏图 ,数据结构类似哈希map,
 //h存放的是图中所有结点(h中每个元素将会拉着'一条链',表示该点可达点),
 //e[i] 表示的是第i条边指向的结点,
 //ne[i] 表示的是第i条边的下一条边
 //w[i] 表示的是第i跳变的权重,
 //idx为当前边的编号。
 int dist[N];//dist[i] 表示第i个点到起点的距离
 bool st[N];//判断第i个点到起点的距离的最短路是否已确定
 int n,m;
 void add(int a,int b,int c){
  e[idx] = b;//idx边所指点是b
  w[idx] = c;//idx边的权值是c
  ne[idx] = h[a];//下面两行表示将b插入到a的可达点链的最前面。
  h[a] = idx++;
 }
 
 int Dijkstra(){
  //思想与宽搜类似
  memset(dist,0x3f,sizeof dist);
  //鉴于题意要求最短路,所以采用小根堆来维护每个点到起点的距离,这样堆顶总是最短的路径
  //因为dijkstra算法是基于贪心的,所以总用堆顶结点(当前所有点中到起点距离最小的点)来更新堆顶可达点到起点的最短路。
  priority_queue<PII,vector<PII>,greater<PII> > p;
  dist[1] = 0;
  p.push({0,1});//把起点信息入堆,起点到自己距离为0,所以插入的是{0,1};
  while(p.size()){
  //堆顶出堆
  auto t = p.top();
  p.pop();
 
  //堆顶可扩展点入堆
  int distance = t.first;
  int ver = t.second;
  //如果ver这个点已近确定了它到起点的最短路,那么可以continue,因为如果它已经确定,那么它的可达点到起点的最短路已经会被它更新,
  //所以,重复操作可以得以避免。
  if(st[ver]) continue;
 
  for(int i=h[ver];i!=-1;i=ne[i]){
  int j = e[i];
  if(dist[j] > distance + w[i]){
  dist[j] = distance + w[i];
  //可扩展点入堆
  p.push({dist[j] , j});
  }
  }
  //ver及其拓展点的最短路信息被更新后就设置ver点的最短路已经确定。
  st[ver] = true;
  }
 
  if(dist[n] == 0x3f3f3f3f) return -1;
  return dist[n];
 }
 int main(){
  scanf("%d%d",&n,&m);
  memset(h,-1,sizeof h);
  while(m--){
  int a,b,c;
  scanf("%d%d%d",&a,&b,&c);
  add(a,b,c);
  }
  int t = Dijkstra();
  cout<<t<<endl;
  return 0;
 }

 

posted @ 2021-05-19 22:39  how_you_make_me_feel  阅读(96)  评论(0编辑  收藏  举报