首页 > 代码库 > SPFA算法O x

SPFA算法O x

 

3、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即可。例题中的参考程序使用了循环队列。
 
技术分享

样例:

7 12
1 2 24
1 3 8
1 4 15
2 5 6
3 5 7
3 6 3
4 7 4
5 7 9
6 7 3
6 4 5
6 5 2
7 2 3
 
 
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 
 5 using namespace std;
 6 const int Maxn=1001,Maxx=999999;
 7 
 8 int que[Maxn],map[Maxn][Maxn],dis[Maxn];
 9 bool cun[Maxn];
10 int n,m;
11 int qianqu[Maxn],q[Maxn];
12 
13 void SPFA(int s)
14 {
15     int head=0,tail=1,v;
16     que[1]=s;   //将s入队 
17     dis[s]=0;    //s to s 的距离为0
18     qianqu[s]=s;  //记录下s的前趋 
19     cun[s]=1;  //进行标记,已经入队 
20     do
21     {
22         v=que[++head];  //取出队头元素 
23         cun[v]=0; //将标记撤销,说明已经出队 
24         for(int i=1;i<=n;i++)
25         {
26             if(dis[i]>dis[v]+map[v][i])  //进行松弛 
27             {
28                 dis[i]=dis[v]+map[v][i];
29                 qianqu[i]=v;   //记录前趋 
30                 if(!cun[i])   //如果队中没有i元素 
31                 {
32                     que[++tail]=i;  //入队 
33                     cun[i]=1;   //进行标记,已经入队 
34                 }
35             }
36         }
37         
38     }while(head<tail);   //进行循环的条件 
39 }
40 
41 void print(int s,int e)
42 {
43     int tot=1;
44     q[tot]=e;
45     tot++;
46     int temp=qianqu[e];
47     while(temp!=s)
48     {
49         q[tot]=temp;
50         tot++;
51         temp=qianqu[temp];
52     }
53     q[tot]=s;
54     for(int i=tot;i>=1;i--)
55     {
56         if(i!=1)
57           cout<<q[i]<<"-->";
58         else
59           cout<<q[i]<<endl;
60     }
61 }
62 
63 int main()
64 {
65     memset(dis,Maxx,sizeof(dis));   //dis与map必须!!!进行初始化 
66     memset(map,Maxx,sizeof(map));   //这样才能够松弛 
67     scanf("%d %d",&n,&m);
68     int q,h,w,s,e;
69     for(int i=1;i<=m;i++)
70     {
71         scanf("%d %d %d",&q,&h,&w);
72         map[q][h]=w;
73     }
74     //memset(cun,0,sizeof(cun));
75     //memset(que,0,sizeof(que));
76     //这两个可以不用进行初始化 
77     scanf("%d %d",&s,&e);
78     SPFA(s);
79     printf("%d\n",dis[e]);
80     print(s,e);
81     return 0;
82 }

 

SPFA算法O x