• 最短路径算法 2.Dijkstra算法


    Dijkstra 算法解决的是带权重的有向图上单源最短路径问题,该算法要求所有边的权重都为非负值。该算法的时间复杂度是O(N2),相比于处理无负权的图时,比Bellmad-Ford算法效率更高。

    算法描述:

    首先引用《算法导论》中的一段比较官方的话,如果可以看懂,那下一部分就可以跳过了:

    “Dijkstra算法在运行过程中维持的关键信息是一组结点集合S。从源结点s到该集合中每个结点之间的最短路径已经被找到。算法重复从结点集 V - S 中算则最短路径估计的最小的结点 u ,将 u 加入到集合S,然后对所有从 u 出发的边进行松弛。” 所谓松弛操作,简单的说就是更新两点间的最短距离。

    不是很好理解对吧,那么下面的描述是更容易理解的一种描述:

    设起始点为s,dis[v]表示s点到v点的最短路径,pre[v]是v的前驱结点,用来输出路径。

    1、初始化:dis[v]=∞(v≠s) dis[s]=0,pre[s]=0;

    2、for(i=1;i<=n;i++)

    (1)在没有被访问过的点中,即上述的V - S集合,找到一个点 u 使得dis[u]是最小的。

    (2)标记 u 为已确定的最短路径。

    (3)for(每个与 u 相连且没有确定过最短距离的点 v)     

    if(dis[u]+m[u][v]<dis[v]){

      dis[v]=dis[u]+m[u][v];

      pre[v]=u;

    }

    3、结束:结果dis[v]就是s到v的最短距离。

    算法理解:

    其中自以为有几点理解需要说明:

    1、为什么用到中间点?

    2、取s到中间点的距离时采用什么策略?

    第一个问题,从起点到另一个点的最短路径至少会经历一个中间点,所以我们要求出经过这个中间的到另一个点的路径,就要先求出起点到中间点的最短路径。

    第二个问题,其实这里采用的是一中贪心的策略。当然这个策略可以被严格证明是正确的,但是我也一知半解,只知道是可以被证明的,在这里也就不浪费时间了。(详细可以参考《算法导论》)

    最后,解释一下为什么有负权边的时候不可以:

    连接矩阵如下(图可以自己在旁边画一下):

      1 2 3

    1 2 1

    2 2 -4

    3 1 -4

    那么第一次标记的点就为3并且把dis[3]记为1,但实际上dis[3]应该时-2,因此就会出现错误。

    最后附上一段不那么标准的代码:

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 int m[100][100],e,dist[100],n,b[100],pre[100],dist[100];
     4 void dij(int s){
     5      b[s]=1;
     6      int i,j;
     7      for(i=1;i<=n;i++)
     8       dist[i]=m[s][i];
     9      dist[s]=0;
    10      pre[s]=0;
    11      
    12      for(i=1;i<=n;i++){
    13       int min=1000000,k=0;
    14       for(j=1;j<=n;j++)
    15        if(b[j]!=1 && dist[j]<min)
    16         {min=dist[j];k=j;}
    17        b[k]=1;
    18        for(j=1;j<=n;j++)
    19         if(min+m[k][j]<dist[j]&&b[j]!=1)
    20          {
    21           dist[j]=min+m[k][j];
    22           pre[j]=i;
    23           }                                
    24       }
    25       for(i=1;i<=n;i++)
    26        if(i!=s)
    27         printf("%d ",dist[i]);
    28      }
    29 int main(){
    30     int i,j;
    31     scanf("%d%d",&n,&e);
    32     memset(b,0,sizeof(b));
    33     memset(m,10000,sizeof(m));
    34     for(i=1;i<=e;i++){
    35      int x,y;
    36      scanf("%d%d",&x,&y);
    37      scanf("%d",&m[x][y]);
    38      }
    39     int w;
    40     scanf("%d",&w);
    41     dij(w);
    42     system("pause");
    43     return 0;
    44     }
  • 相关阅读:
    C++中使用多线程
    hdu 4223 dp 求连续子序列的和的绝对值最小值
    hdu 1372 bfs 计算起点到终点的距离
    hdu 4217 线段树 依次取第几个最小值,求其sum
    心得
    hdu 1175 bfs 按要求进行搜索,是否能到达,抵消两个(相同)棋子
    hdu 4221 greed 注意范围 工作延期,使整个工作时间罚时最少的单个罚时最长的值
    hdu 2844 多重背包 多种硬币,每一种硬币有一点数量,看他能组成多少种钱
    uva LCDDisplay
    hdu 4218 模拟 根据一个圆点和半径画一个圆 注意半径要求
  • 原文地址:https://www.cnblogs.com/uncklesam7/p/8831204.html
Copyright © 2020-2023  润新知