SPFA算法O(kE)
主要思想是:
初始时将起点加入队列。每次从队列中取出一个元素,并对所有与它相邻的点进行修改,若某个相邻的点修改成功,则将其入队。直到队列为空时算法结束。
这个算法,简单的说就是队列优化的bellman-ford,利用了每个点不会更新次数太多的特点发明的此算法。
SPFA 在形式上和广度优先搜索非常类似,不同的是广度优先搜索中一个点出了队列就不可能重新进入队列,但是SPFA中一个点可能在出队列之后再次被放入队列,也就是说一个点修改过其它的点之后,过了一段时间可能会获得更短的路径,于是再次用来修改其它的点,这样反复进行下去。
算法时间复杂度:O(kE),E是边数。K是常数,平均值为2。
算法实现:
dis[i]记录从起点s到i的最短路径,w[i][j]记录连接i,j的边的长度。pre[v]记录前趋。
team[1..n]为队列,头指针head,尾指针tail。
布尔数组exist[1..n]记录一个点是否现在存在在队列中。
初始化:d[s]=0,d[v]=∞(v≠s),memset(exist,false,sizeof(exist));
起点入队team[1]=s; head=0; tail=1;exist[s]=true;
do
{1、头指针向下移一位,取出指向的点u。
2、exist[u]=false;已被取出了队列
3、for与u相连的所有点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\n",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 }
转载于:https://www.cnblogs.com/mjtcn/p/6689624.html