• Dijkstra算法详解


    前言

    前几天研究的Bellman_Ford算法虽然可以算负权,可是时间复杂度高达O(NM),即使是采用了队列优化,也有可能被网格图卡回O(NM),所以今天我们就来研究一个新的,更快的,但同时只能在正权图上运行的算法:Dijkstra(朴素Dijkstra算法)

    Dijkstra基本思想及实现过程

    我们首先需要以下几个数组:dist[],vis[],用邻接矩阵需要g[][],邻接表则需要v[],w[],head[],nxt[]

    邻接表与邻接矩阵在此不做过多解释,不懂的同学请自行百度,dist[i]表示i离源点的最短路距离,vis[i]==1就表示i号节点已经永久标号(之后会详细解释什么是永久标号)

    算法步骤:

    1)将源点距离初始化为0,其它点为正无穷INF

    2)经过n次如下操作,得到源点离其它点的最短距离:

    1、选择一个未扩展的点k,满足dist[k]是未扩展节点中离源点距离最小的;

    2、对k进行永久标号

    3、以k为中间点修改源点到其它点的最短路距离

    时间复杂度O(N2),由于所有边权都为正,从而保证了算法的正确性。

    朴素Dijkstra(邻接矩阵)

    通过上边的步骤依次实现即可,下面给出参考程序:

    Dijkstra堆优化(邻接表+优先队列)

     1 #include<iostream>
     2 #include<cstring>
     3 #define inf 336860180
     4 using namespace std;
     5 int n,m,x,y,w,map[1000][1000],minn,dist[1000],t;
     6 bool pd[1000];
     7 int main()
     8 {
     9     memset(map,20,sizeof(map));
    10     memset(dist,20,sizeof(dist));
    11     cin>>n>>m;
    12     for(int i=1;i<=n;i++)
    13     {
    14         map[i][i]=0;
    15     }
    16     for(int i=1;i<=m;i++)
    17     {
    18         cin>>x>>y>>w;
    19         map[x][y]=w;
    20     }
    21     dist[1]=0;
    22     pd[1]=1;
    23     for(int i=1;i<=n;i++)
    24     {
    25         t=1;minn=9999999;
    26         for(int j=1;j<=n;j++)
    27         {
    28             if(pd[j]==0&&dist[j]<=minn)
    29             {
    30                 minn=dist[j];
    31                 t=j;
    32             }
    33         }
    34         pd[t]=1;
    35         for(int j=1;j<=n;j++)
    36         {
    37             dist[j]=min(dist[j],dist[t]+map[t][j]);
    38         }
    39     }
    40     for(int i=1;i<=n;i++)
    41     {
    42         if(i-1)cout<<" ";
    43         if(dist[i]!=inf)cout<<dist[i];
    44         else cout<<"INF";
    45     }
    46     return 0;
    47 }
    View Code

    由于每次查找最短的点浪费大量时间,我们可以用优先队列进行查找,用pair记录,第一维记录最短距离,第二维记录点的编号,创建一个小根堆,每次取堆顶扩展即可。时间复杂度降为O((n+m)logm),参考程序如下:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<queue>
     4 #include<cstring>
     5 using namespace std;
     6 typedef pair<long long,long long>P;
     7 long long n,m,s,dist[100001],v[200005],w[200005],nxt[200005],head[200005],cnt,x,y,z;
     8 bool vis[100001];
     9 void add(long long a,long long b,long long c)
    10 {
    11     v[++cnt]=b;
    12     w[cnt]=c;
    13     nxt[cnt]=head[a];
    14     head[a]=cnt;
    15 }
    16 void dijkstra(int s)
    17 {
    18     memset(dist,20,sizeof(dist));
    19     priority_queue<P,vector<P>,greater<P> >q;
    20     dist[s]=0;
    21     q.push(P(0,s));
    22     while(!q.empty())
    23     {
    24         long long c=q.top().second;
    25         q.pop();
    26         if(vis[c])continue;
    27         vis[c]=1;
    28         for(int i=head[c];i;i=nxt[i])
    29         {
    30             int y=v[i];
    31             if(dist[y]>=dist[c]+w[i])
    32             {
    33                 dist[y]=dist[c]+w[i];
    34                 q.push(P(dist[y],y));
    35             }
    36         }
    37     }
    38 }
    39 int main()
    40 {
    41     scanf("%d%d%d",&n,&m,&s);
    42     for(int i=1;i<=m;i++)
    43     {
    44         scanf("%d%d%d",&x,&y,&z);
    45         add(x,y,z);
    46     }
    47     dijkstra(s);
    48     for(int i=1;i<=n;i++)
    49     {
    50         cout<<dist[i]<<" ";
    51     }
    52     return 0;
    53 }
    View Code

      

  • 相关阅读:
    树上DP
    区间dp
    [持续更新]学习并记忆 Linux 下的常用的命令
    区间dp
    codevs 2152
    树上DP入门题
    差分与前缀和
    POJ
    VJ
    Week 5: Object Oriented Programming 9. Classes and Inheritance Exercise: int set
  • 原文地址:https://www.cnblogs.com/szmssf/p/10980237.html
Copyright © 2020-2023  润新知