关于最短路(1)

最短路的定义

我们可以把边带有权值的图称为带权图。边的权值可以理解为两点之间的距离。
一张图任意两点间会有不同的路径相连。最短路径就是指连接两点的这些路径中最短的一条。

最短路的算法

我们有四种方法求出两个点间最短的路径
分别是
Floyed-Warshall算法(简称Floyed算法)
Dijkstra算法
Bellman-Ford算法(简称Ford算法)
SPFA算法

注意,当出现负边权时,有些方法并不适用。

今天,我们来讲讲SPFA算法。
**

1.SPFA算法的定义

**
SPFA算法是Bellman-Ford算法的一种队列实现(就是队列优化的Ford算法),减少了不必要的计算。
SPFA和广搜BFS类似,但是不同的是,BFS中出队列的元素不可能进入队列。而SPFA中每个点可以重复进出队列,被再次用来修改其他的点。
SPFA算法是一种效率很高的算法,它时间复杂度为O(kE),k为常数,E为边数。

2.SPFA算法的实现

我们定义一个dis数组,dis[i]表示从起点至i点的最短路径,
flag[u]表示u点是否存在于队列中。
初始化:

#define INF INT_MAX
void start(int n)
 {
  for(int i=1;i<=n;i++)
  dis[i]=INF;
 }

开始SPFA算法
将起点u放入队列,并标记u点存在于队列中,并把头指针向后移动一位
且dis[u]=0;
for循环查询与u相连的所有点v,依次访问每个点,并比较路径长度
如果更小,则入队,记录路径长度。

void solve(int p)
 {
  q.push(p);
  dis[p]=0;
  flag[p]=true;
  while(!q.empty())
  {
   int u=q.front();
   q.pop();
   flag[u]=false;
   for(int i=now[u];i;i=pre[i])
   {
    int v=son[i];
    if(dis[v]>dis[u]+len[i])
    {
     dis[v]=dis[u]+len[i];
     if(!flag[v])
     {
      flag[v]=false;
      q.push(v);
     }
    }
   }
  }
 } //我用的是链式前向星存图,也可使用邻接矩阵存图。

3.输出最短路径长度

由起点到终点N的最短路径为dis[N],所以,易得代码如下

void print(int p)
 {
  printf("%d\n",dis[p]);
 }

完整SPFA代码(包括链式前向星)如下

#define maxn 200001
#define INF INT_MAX
int pre[maxn*2],now[maxn*2],son[maxn*2],len[maxn];
int n,m,p,e,tot=0,x,y;
int dis[maxn];
bool flag[maxn];
queueq;
inline void edge(int x,int y,int z)
{
    pre[++tot]=now[x];
    now[x]=tot;
    son[tot]=y;
    len[tot]=z;
}
struct SPFA
{
 void start(int n)
 {
  for(int i=1;i<=n;i++)
  dis[i]=INF;
 }
 void solve(int p)
 {
  q.push(p);
  dis[p]=0;
  flag[p]=true;
  while(!q.empty())
  {
   int u=q.front();
   q.pop();
   flag[u]=false;
   for(int i=now[u];i;i=pre[i])
   {
    int v=son[i];
    if(dis[v]>dis[u]+len[i])
    {
     dis[v]=dis[u]+len[i];
     if(!flag[v])
     {
      flag[v]=false;
      q.push(v);
     }
    }
   }
  }
 } 
 void print(int p)
 {
  printf("%d\n",dis[p]);
 }
}spfa;

一些容易上手的题目

洛谷P3371 单源最短路径(弱化版)
入门oj 1652 最短路径问题

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