spfa算法O(kE)

SPFA是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。
主要思想是:
初始时将起点加入队列。每次从队列中取出一个元素,并对所有与它相邻的点进行修改,若某个相邻的点修改成功,则将其入队。直到队列为空时算法结束。
这个算法,简单的说就是队列优化的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]记录一个点是否现在存在在队列中。
初始化:dis[s]=0,dis[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 (dis[v]>dis[u]+w[u][v])
{
dis[v]=dis[u]+w[u][v];
pre[v]=u;
if (!exist[v]) //队列中不存在v点,v入队。
{
//尾指针下移一位,v入队;
exist[v]=true;
}

}
while (head < tail);
循环队列:
  采用循环队列能够降低队列大小,队列长度只需开到2*n+5即可。

(未用循环队列优化)

#include
#include
#include
#include
using namespace std;
int n,i,j,f[101][101],x,y,head,tail,dis[101],h[101];
bool b[101];

void spfa()
{
    head=1;
    tail=1;
    h[1]=x;
    dis[x]=0;
    b[x]=true;
    do
      {
        for (i=1;i<=n;i++)
            {
              if (dis[i]>dis[h[head]]+f[h[head]][i])
                {
                  dis[i]=dis[h[head]]+f[h[head]][i];
                  if (!b[i])
                    {
                      tail++;
                      h[tail]=i;
                      b[i]=true;
                    }
                }
            }
        b[h[head]]=false;
        head++;
      }while (head<=tail);
}

int main()
{
    scanf("%d",&n);
    for (i=1;i<=n;i++)
      for (j=1;j<=n;j++)
        scanf("%d",&f[i][j]);
    for (i=1;i<=n;i++)
      dis[i]=INT_MAX;
    memset(b,false,sizeof(b));
    scanf("%d %d",&x,&y);
    spfa();
    printf("%d",dis[y]);
    return 0;
}

你可能感兴趣的:(最短路)