概念
SPFA(Shortest Path Faster Algorithm) 和 Dijkstra算法 可谓是 单源最短路 的两大双子星了。
SPFA 由前身 Bellman-Ford-Moore(Bellman-Ford或BFM)算法添加一个 队列 来实现优化。
尽管如此,SPFA的复杂度最劣情况仍旧是BFM算法的复杂度,毕竟本质上只是多了一个优化的操作。
BFM算法
要知道,Richard Bellman(理查德.贝尔曼)是 动态规划 的创始人。
而BFM算法,正是运用了动态规划的思想。
相较于采用贪心思想的Dijkstra算法的无法处理 负权边,
动态规划显然没有这个限制。
松弛
那么状态是怎么转移的呢?
我们记从源点到每个节点i的距离为 dis[i](f[i])。(dis=distance 距离)
对于已知的当前节点i的的dis[i],记其要转移的下一个节点j,有边权w[i][j]。(w=weight 权值)
那么显然有 dis[j] = min( dis[i]+w[i][j] , dis[j] )。
其实介个操作就叫 松弛 啦。
而且,当我们发现对于每个点都没法更优时,说明我们已经得到了最优解,
那么我们可以直接跳出,以优化时间。
初始化
由于我们需要的是最小值,
那么除了源点本身,其余都应该初始化为 +∞ 无穷大(INF)
而源点本身到自己的距离自然是0。
时间复杂度
显然我们需要枚举除了终点的每个 i 和 j,
故其 时间复杂度 大概为 节点数N乘以边数M,即 O(NM)。
SPFA
进行优化
我们说过了,我们可以对BFM算法加入一个队列来进行优化。
我们可以在松弛时,如果该点j可以使得当前路径更短,并且其不在队列中,再将其加入队列。
因为,如果其已在队列中,我们对其再加入队列的访问是多余的。
因为,如果后面可以继续使得路径更短,它就已经会被加入队列。
所以,我们考虑加入一个 vis[i] 来表示其是否在队列中。(vis=visited 已访问)
所以对于该点,如果有负环,显然对其的访问次数会超过N(节点数)。
那么我们可以考虑加入一个count[i],统计每个点的访问次数,一旦超过N,说明该图存在负环。
显然,如果存在负环,那么最后最短路的结果会是负无穷。。。
于是乎,对于存在负环时,我们使函数返回 0。
模板代码
SPFA
这里大概打一下,不保证你copy过去能过。
模板还是自己打最好啦,大佬们的代码比我更好,毕竟你大佬还是你大佬。
bool SPFA(int start) 当图存在负环时,我们返回 0
{
memset