zoukankan      html  css  js  c++  java
  • 图论-最短路径--3、SPFA算法O(kE)

    SPFA算法O(kE)
    主要思想是:
        初始时将起点加入队列。每次从队列中取出一个元素,并对所有与它相邻的点进行修改,若某个相邻的点修改成功,则将其入队。直到队列为空时算法结束。
        这个算法,简单的说就是队列优化的bellman-ford,利用了每个点不会更新次数太多的特点发明的此算法。
    SPFA 在形式上和广度优先搜索非常类似,不同的是广度优先搜索中一个点出了队列就不可能重新进入队列,但是SPFA中一个点可能在出队列之后再次被放入队列,也就是说一个点修改过其它的点之后,过了一段时间可能会获得更短的路径,于是再次用来修改其它的点,这样反复进行下去。
    算法时间复杂度:O(kE)E是边数。K是常数,平均值为2
     
    算法实现:
        dis[i]记录从起点si的最短路径,w[i][j]记录连接ij的边的长度。pre[v]记录前趋。
        team[1..n]为队列,头指针head,尾指针tail
        布尔数组exist[1..n]记录一个点是否现在存在在队列中。
        初始化:d[s]=0,d[v]=∞(vs),memset(exist,false,sizeof(exist));
        起点入队team[1]=s; head=0; tail=1;exist[s]=true;
        do
        {1、头指针向下移一位,取出指向的点u
        2、exist[u]=false;已被取出了队列
        3、foru相连的所有点v  //注意不要去枚举所有点,用数组模拟邻接表存储
           if (d[v]>d[u]+w[u][v])
             {   d[v]=d[u]+w[u][v];
                 pre[v]=u;
                 if (!exist[v]) //队列中不存在v点,v入队。
                   {         //尾指针下移一位,v入队;
                        exist[v]=true;
                     }
              }
        }
        while (head < tail);
    循环队列:
      采用循环队列能够降低队列大小,队列长度只需开到2*n+5即可。例题中的参考程序使用了循环队列。
     
     1 #include<iostream>
     2 #include<cstdio>
     3 #define N 2010
     4 #include<cstring>
     5 using namespace std;
     6 int dis[N];  //起点到其他点的最短路径 
     7 int pre[N];  //前驱 
     8 int map[N][N];  //两点之间距离 
     9 int ans[N];  //输出 
    10 int team[N];  //队列 
    11 bool pd[N];  //判断是否在队列中 
    12 int head,tail,n,m,from,to;  
    13 void work(int a)
    14 {
    15     team[tail++]=a;
    16     pre[a]=a;
    17     dis[a]=0;
    18     pd[a]=1;
    19     while(head<tail)
    20     {
    21         int d=team[head];  //取出队首元素
    22         for(int i=1;i<=n;++i)
    23             if(map[d][i]!=0&&dis[i]>dis[d]+map[d][i])
    24             {
    25                 dis[i]=dis[d]+map[d][i];
    26                 pre[i]=d;
    27                 if(!pd[i])
    28                 {
    29                     team[++tail]=i;
    30                     pd[i]=1;
    31                 }
    32             }
    33         head++;
    34         pd[d]=0;
    35     }
    36     printf("%d
    ",dis[to]);    
    37 }
    38 void print(int a,int b)
    39 {
    40     ans[1]=to;
    41     int top=1;
    42     int t=b;
    43     while(t!=from)
    44     {
    45         t=pre[t];
    46         ans[++top]=t;
    47     }
    48     for(int i=top;i>=2;--i)
    49         printf("%d->",ans[i]);
    50     printf("%d",ans[1]);
    51 }
    52 int main()
    53 {
    54     memset(dis,0x7f,sizeof(dis));  //初始化 
    55     cin>>n>>m;
    56     for(int i=1;i<=m;++i)
    57     {
    58         int x,y,q;
    59         scanf("%d%d%d",&x,&y,&q);
    60         map[x][y]=q;  //有向图 
    61     }
    62     cin>>from>>to;  //需要计算的两点 
    63     work(from);
    64     print(from,to);
    65     return 0;
    66 }
  • 相关阅读:
    缓存
    判断空对象的方法
    Vue响应式系统如何操作运用?本文详解
    2020最新中级web前端面试题库(含详细答案,15k级别)你会几道呢?
    如何用JavaScriptJ封装拖动验证滑块?本文教你
    Nodejs中ES Modules如何操作运用?本文详解
    Vue学习总结之Vue的生命周期是怎么运用操作的?本文详解
    Vue项目如何部署?实战教你
    canvas绘制简单的霓虹灯效果
    canvas绘制五角星详细过程
  • 原文地址:https://www.cnblogs.com/mjtcn/p/6689624.html
Copyright © 2011-2022 走看看