最短路的定义
我们可以把边带有权值的图称为带权图。边的权值可以理解为两点之间的距离。
一张图任意两点间会有不同的路径相连。最短路径就是指连接两点的这些路径中最短的一条。
最短路的算法
我们有四种方法求出两个点间最短的路径
分别是
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];
queue<int>q;
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;