zoukankan      html  css  js  c++  java
  • 最短路—SPFA

     
    求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm。
    SPFA算法是西南交通大学段凡丁于1994年发表的.
    很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。
       我们用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。
    我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。
    这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
      定理: 只要最短路径存在,上述SPFA算法必定能求出最小值。
       期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。 
       实现方法:建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。

    重复执行直到队列为空

    判断有无负环:如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

    模版:

    View Code
     1 #include <iostream>
     2  #include <cstdio>
     3  #include <cstring>
     4  using namespace std;
     5  typedef struct node
     6  {
     7      long adj,data;/*adj为邻接点,data为边的权值*/
     8      struct node *next;
     9  }node;
    10  node *g[1000];
    11  long dis[1000],flag[1000],q[1000],n,e;/*dis记录最短路径值,flag做出入队标记,q为队列*/
    12  void spfa(long st)
    13  {
    14      long i,k,front,rear;
    15      node *p;
    16      for(i=0;i<n;i++) 
    17          dis[i]=0x7fffffff;/*初始化路径为长整型最大数*/
    18      memset(flag,0,sizeof(flag));/*入队标记初始化为0*/
    19      front=0;
    20      rear=1;
    21      /*将起始点入队*/
    22      dis[st]=0;/*到自己距离为0*/
    23      q[rear]=st;/*入队*/ 
    24      flag[st]=1;/*置入队标记*/
    25      while(front!=rear)/*当队列不为空时*/
    26      {     
    27          front=(front+1)%1000;/*出队,注意使用循环队列*/
    28          k=q[front];/*记下出队点号*/
    29          flag[k]=0;/*置出队标记*/
    30          p=g[k];/*找到以出队点为头的邻接链表,考察由出队点中转至各邻接点路径是否更短*/
    31          while(p)
    32          {
    33              if(dis[k]+p->data<dis[p->adj])
    34              { 
    35                  dis[p->adj]=dis[k]+p->data;/*路径更短,更新路径值*/
    36                  if(!flag[p->adj])/*若其邻接点未入队*/
    37                  { 
    38                      rear=(rear+1)%1000;/*入队*/
    39                      q[rear]=p->adj;
    40                      flag[p->adj]=1;/*置入队标记*/
    41                  }
    42              }
    43              p=p->next;/*记得改变循环变量,考察下一个邻接点*/
    44          }
    45      }  
    46  }
    47  int main()
    48  {
    49      long i,x,y,z;
    50      node *p;
    51      scanf("%ld%ld",&n,&e);/*读入点数,边数*/
    52      memset(g,0,sizeof(g));/*初始化图*/
    53      for(i=1;i<=e;i++)/*读入边的信息,左点、右点、权值*/
    54      {
    55          scanf("%ld%ld%ld",&x,&y,&z);
    56          p=(node*)malloc(sizeof(node));/*创建链表*/
    57          p->data=z;
    58          p->adj=y;
    59          p->next=g[x];/*尾插法*/
    60          g[x]=p;
    61      }
    62      spfa(0);/*计算由起始点到各点的最短路径*/
    63      for(i=0;i<n;i++)
    64          printf("%ld ",dis[i]);
    65      printf("\n");
    66      return 0;
    67  }

    poj3259

    View Code
      1 View Code 
      2  #include <iostream>
      3  #include <cstring>
      4  #include <cstdlib>
      5  #include <cstdio>
      6  #include <queue>
      7  using namespace std;
      8  
      9  const int N=501;
     10  const int NN=100001;
     11  const int inf=0x7fffffff;
     12  queue<int> qu;
     13  int n,nu;
     14  
     15  typedef struct node
     16  {
     17      int adj,val;
     18      struct node *next;
     19  }node;
     20  node node[NN],*p[N];
     21  
     22  int SPFA()
     23  {
     24      int x,i,a,b;
     25      int vis[N],dis[N],num[N];
     26      struct node *head[N];
     27      for(i=1;i<=n;i++)
     28      {
     29          vis[i]=0;
     30          num[i]=0;
     31          dis[i]=inf;
     32          head[i]=p[i];
     33      }
     34      dis[1]=0;
     35      vis[1]=1; 
     36      num[1]++;
     37      qu.push(1);
     38      while(!qu.empty())
     39      {
     40          x=qu.front();
     41          qu.pop();
     42          vis[x]=0;
     43          head[x]=p[x];
     44          while(head[x])
     45          {
     46              a=head[x]->adj;
     47              b=head[x]->val;
     48              if(dis[a]>dis[x]+b)
     49              {
     50                  dis[a]=dis[x]+b;
     51                  if(!vis[a])
     52                  {
     53                      qu.push(a);
     54                      vis[a]=1;
     55                      num[a]++;
     56                      if(num[a]>=n)//如果入队的次数超过总数,说明存在回路 
     57                          return 1;
     58                  }
     59              }
     60              head[x]=head[x]->next; 
     61          }
     62      }
     63      return 0;
     64  }
     65  
     66  int main()
     67  {
     68      int t,i,m,w,a,b,c;
     69      scanf("%d",&t);
     70      while(t--)
     71      {
     72          while(!qu.empty()) 
     73              qu.pop();
     74          memset(node,0,sizeof(node));
     75          memset(p,0,sizeof(p));
     76          nu=0;
     77          scanf("%d%d%d",&n,&m,&w);
     78          for(i=0;i<m;i++)
     79          {
     80              scanf("%d%d%d",&a,&b,&c);
     81              node[nu].adj=b;  
     82              node[nu].val=c;
     83              node[nu].next=p[a];  
     84              p[a]=&node[nu];  
     85              nu++;  
     86              node[nu].adj=a;  
     87              node[nu].val=c;  
     88              node[nu].next=p[b];  
     89              p[b]=&node[nu];  
     90              nu++; 
     91          }
     92          for(i=0;i<w;i++)
     93          {
     94              scanf("%d%d%d",&a,&b,&c);
     95              node[nu].adj=b;  
     96              node[nu].val=-c;
     97              node[nu].next=p[a];
     98              p[a]=&node[nu];  
     99              nu++;  
    100          }
    101          if(SPFA()) 
    102              puts("YES");
    103          else 
    104              puts("NO");
    105      }
    106      return 0;
    107  }

     我的代码

    View Code
     1 #include <stdio.h>
     2 #include <string.h>
     3 #define maxn 500*10000+50;
     4 int map[505][505];
     5 int vis[505];//是否在队伍中
     6 int num[505];//记录进队的次数
     7 int dis[505];
     8 int q[500005],f,r;
     9 void init(int n)
    10 {
    11     int i,j;
    12     for(i = 1;i <= n;i++)
    13     {
    14         dis[i] = maxn;
    15         for(j = 1;j <= n;j++)
    16             map[i][j] = maxn;
    17     }
    18     
    19 }
    20 int spfa(int n)
    21 {
    22     memset(num,0,sizeof(num));
    23     memset(vis,0,sizeof(vis));//初始化
    24     f = r = 0;
    25     int i;
    26     q[r++] = 1;
    27     num[1] = 1;
    28     vis[1] = 1;//进队
    29     dis[1] = 0;//离自己的距离为0
    30     while(f < r)
    31     {
    32         int x;
    33         x = q[f++];
    34         vis[x] = 0;
    35         for(i = 1;i <= n;i++)
    36         {
    37             if(dis[i] > dis[x]+map[x][i])//跟dis差不多。
    38             {
    39                 dis[i] = dis[x]+map[x][i];
    40                 if(!vis[i])
    41                 {
    42                     vis[i] = 1;
    43                     q[r++] = i;
    44                     num[i]++;
    45                     if(num[i] == n)//如果入队次数等于n说明有负环
    46                     return 1;
    47                 }
    48             }
    49         }
    50     }
    51     return 0;
    52 }
    53 int main()
    54 {
    55     int T,i,j,n,m,w;
    56     scanf("%d",&T);
    57     while(T--)
    58     {
    59         int a,b,c;
    60         scanf("%d %d %d",&n,&m,&w);
    61         init(n);
    62 
    63         for(i = 1;i <= m;i++)
    64         {
    65             scanf("%d %d %d",&a,&b,&c);
    66             if(map[a][b] > c)
    67             map[a][b] = map[b][a] = c;
    68         }
    69         while(w--)
    70         {
    71             scanf("%d%d%d",&a,&b,&c);
    72             if(map[a][b] > -c)
    73             map[a][b] = -c;
    74         }
    75         int leap;
    76         leap = spfa(n);
    77         if(leap)
    78         printf("YES\n");
    79         else
    80         puts("NO");
    81     }
    82     return 0;
    83 }
  • 相关阅读:
    mysql基础知识
    项目开发步骤
    python 中的电子邮箱的操作
    django 运行python manage.py sqlall books 时报错 app has migration
    mysql报错锦集
    搭建spark中需要注意的问题
    pycharm5.0 激活方式
    python去除停用词(结巴分词下)
    Ubuntu下安装libsvm
    PCIE_DMA实例五:基于XILINX XDMA的PCIE高速采集卡
  • 原文地址:https://www.cnblogs.com/0803yijia/p/2640267.html
Copyright © 2011-2022 走看看