首先理解单源最短路一滴概念(以下概念不作证明,证明自行baidu):
V:点集
E:边集
s:源点
u:当前起始定点
v:当前目标顶点
u->v:表示顶点u直接到达顶点v(经过边数==1)
u~>v:表示顶点u通过某一路径到达顶点v(经过边数>=1)
lo[u, v]:表示顶点u到顶点v既最短路径
d[v]:储存源点s到顶点v既最短路径估计值,下界就系lo[u, v]
w[u,v]:从u到v既一条直达路径既长度(边权)
lo[s, v] <= lo[s, u] + w[u, v]:三角不等式1
d[v] > d[u] + w[u, v]:三角不等式2
P<v0, v1, ......vk>:顶点v0到顶点vk既最短路径点集
松弛技术:当三角不等式2未到达下界(即等于)时,可以对d[v]进行松弛,即执行d[v] = d[u] + w[u,v]
松弛路径性质:P<v0, v1, v2, ........vk>为s到u既最短路径点集(v0为s, vk为u),如果p按照边(v0, v1), (v1, v2)..(vk - 1, vk)顺序进行松弛,呢个时候d[vk] = lo[s, vk]。
-------------------------------------------------------银蛋阉割线---------------------------------------------------------------
SPFA(Shoetest-Path-Faster-Algorithm),时间复杂度底编程复杂度超低,速度更加系唔系人甘品。
作为一种单源最短路算法,潮就潮在,功能上可以代替传统Dijkstra同埋Bellman-Ford。
点解可以甘样讲?
(以下对比前提:链接表储存方式)
传统Dijkstra通过两重循环,对每一个当前d[u]最小者连接既边进行松弛,每个点仅执行一次,呢个算法费时就费在每次揾d[u]最小者都要遍历一次成个点集V,即系费时O(|V|),仲有Dijkstra吾可以用于有负回路既图上面。
Bellman-Ford速度明显最慢,因为该算法硬性对每一条边进行|V| - 1次松弛检测,不过呢个算法可以系有负回路既图上用,因为呢个算法既核心系松弛路径性质,每条最短路最多有|V| - 1条边,所以每条边最多松弛|V| - 1次,如果进行第|V|次松弛检测既时候有一条边可以被松弛,就说明有负回路存在,所以可以判断差分约束系统有无解。
SPFA算法系采用队列同埋松弛技术,每次从队列头取出一点u,对u可直达既点v进行松弛检测,如果松弛成功,检查点v系唔系已经入队列,如果未入,将v压入队列尾,进行以上操作直到队列为空或者有一点进入队列次数为|V|。
SPFA可以代替传统Dijkstra系因为每次只将被松弛点放入队列,当无点被松弛既时候,即每一个d[u] == lo[s, u]。极大程度上减少不必要既松弛操作,速度比传统Dijkstra快。
代替Bellman-Ford系因为SPFA同样适用于路径松弛性质,当有一点进入队列次数为|V|既时候,同样可以证明有负回路存在。
下面比出无负回路图SPFA代码加注释:
#include <iostream>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <memory.h>
using namespace std;
#define MAXI 1011
#define INFI 0x7fffffff
#define LLD long long
struct V
{
int v, w; //v, w[u, v]
};
vector<V> g[MAXI]; //邻接表
int n, dis[MAXI]; //|V|, d[]
bool inQ[MAXI]; //入队标记
void SPFA(int u) //源点
{
int i, v, w;
queue<int> Q; //队列
for (i = 0; i < n; i++) //初始化
{
dis[i] = INFI;
inQ[i] = false;
}
Q.push(u); //压入源点
inQ[u] = true;
dis[u] = 0;
while (!Q.empty()) //队列不为空
{
u = Q.front();
Q.pop();
inQ[u] = false;
for (i = 0; i < g[u].size(); i++)
{
v = g[u][i].v;
w = g[u][i].w;
if (dis[v] - w > dis[u]) //符合三角不等式2
{
dis[v] = w + dis[u]; //松弛操作
if (inQ[v] == false) //判断v系唔系队列
{
Q.push(v); //唔系就入队列
inQ[v] = true;
}
}
}
}
}
下面比一个例子:
就系甘样,其实就系大家非常熟悉既BFS,另外再加上松弛技术姐嘛。
负回路版本只需要加上一个数组记录每个点入队次数就得。
多谢收睇~~