Bellmen算法用来解决单源最短路问题,即给定图G和起点s,通过算法得到s到其他每个点的最短距离。它可以处理Dijkstra不能处理的带负权边图
SPFA是它的优化版本。
Bellmen通过
V
−
1
V-1
V−1(
V
V
V是顶点个数)此迭代,每次遍历所有边:对于每条边
u
−
>
v
u->v
u−>v,如果以
u
u
u为中介点能使d[v]更小,则更新
d
[
v
]
d[v]
d[v]。如果第
n
n
n此迭代,仍有可松弛的
d
[
]
d[]
d[],则说明图内存在负环。时间复杂度是
O
(
V
E
)
O(VE)
O(VE)
简单证明思路:把源点s想象成树的根节点,最短路确定后,因为对于不存在负环的图,最短路中一条边最多走一次,一棵最短路数也确定下来了,如图:
一开始
d
[
s
]
=
0
d[s]=0
d[s]=0第一层
s
s
s被确定。第一次迭代,第二层
B
,
C
B,C
B,C被确定下来,第二次第三层
D
,
E
D,E
D,E被确定下来。由于只有
V
V
V个点,层数不会超过
V
V
V层,迭代次数不会超过
V
−
1
V-1
V−1次。证毕。
Bellmen_Ford代码
const int maxn=1e3+10;
struct ac{
int v;int d;
ac(int _v=0,int _d=0){
v=_v;
d=_d;
}
};
vector<ac> g[maxn];
int d[maxn];
int n;
bool Bellmen_Ford(int s){
memset(d,0x3f,sizeof d);
d[s]=0;
for(int i=0;i<n-1;i++){
for(int u=1;u<=n;u++){
for(int j=0;j<g[u].size();j++){
int v=g[u][j].v;
int dis=g[u][j].d;
if(d[v]>d[u]+dis){
d[v]=d[u]+dis;
}
}
}
}
for(int u=1;u<=n;u++){ //判断是否存在负环
for(int j=0;j<g[u].size();j++){
int v=g[u][j].v;
int dis=g[u][j].d;
if(d[v]>d[u]+dis){
return false;
}
}
}
return true;
}
spfa使用队列,只对于上一次迭代过松弛的点,加入队列中,下一次遍历它的出边。在大部分数据中这个算法异常高效,并且经常优于堆优化的Dijkstra。无负环的时间复杂度是
O
(
K
E
)
O(KE)
O(KE),
K
K
K的期望不超过2。但是在有负环时,时间复杂度退化到
O
(
V
E
)
O(VE)
O(VE)。
SPFA代码
const int maxn=1e3+10;
struct ac{
int v,d;
ac(int _v=0,int _d=0){
v=_v;
d=_d;
}
};
vector<ac> g[maxn];
int d[maxn];
int n;
int inq[maxn];
int inqcnt[maxn];
bool SPFA(int s){
memset(inq,false,sizeof inq);
memset(d,0x3f,sizeof d);
memset(inqcnt,0,sizeof inqcnt);
queue<int> Q;
Q.push(s);
inq[s]=true;
d[s]=0;
inqcnt[s]++;
while(!Q.empty()){
int u=Q.front();
Q.pop();
inq[u]=false;
for(int i=0;i<g[u].size();i++){
int v=g[u][i].v;
int dis=g[u][i].d;
if(d[v]>d[u]+dis){
d[v]=d[u]+dis;
if(inq[v]==false){
Q.push(v);
inq[v]=true;
inqcnt[v]++;
if(inqcnt[v]>=n) return false; //有可达负环
}
}
}
}
return true; //无可达负环
}
二维点状图SPFA
#include<stdio.h>
#include<iostream>
#include<queue>
#include<string.h>
#include<stack>
using namespace std;
const int maxn=1e5+7;
bool inq[maxn][maxn];
char g[maxn][maxn];
int d[maxn][maxn];
//移动方式
int X[]={1,-1,0,0};
int Y[]={0,0,1,-1};
int n,m;
void bfs(int sx,int sy){
queue<pair<int,int> >q;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
d[i][j]=10000000;
}
}
memset(inq,0,sizeof inq);
inq[sx][sy]=1;
q.push(make_pair(sx,sy));
d[sx][sy]=0;
while(!q.empty()){
int ox=q.front().first,oy=q.front().second;
q.pop();
inq[ox][oy]=0;
for(int i=0;i<4;++i){
int tx=ox+X[i],ty=oy+Y[i];
//'#'为障碍物
if(0<tx&&tx<=n&&0<ty&&ty<=m&&g[tx][ty]!='#'&&inq[tx][ty]==0){
if(d[tx][ty]>1+d[ox][oy]){
d[tx][ty]=1+d[ox][oy];
inq[tx][ty]=1;
q.push(make_pair(tx,ty));
}
}
}
}
}