Bellman-Ford
Bellman-Ford
- 本质:DP,对边进行操作
- 特点:单源最短路,求解一个源点到其他所有点的最短距离
- 适用对象:小图,允许负权有向图,不能处理负权无向图和和负环图(负环:图上边权之和为负的环)
- 存储结构:直接存边
- 核心思想:每轮中反复松弛所有边,若该边使距离更优则更新。最多进行 V − 1 V-1 V−1轮,若之后还有边在更新,说明存在负环。
- 算法流程:闫氏DP分析法
- 状态表示:
- 集合:定义源点 s s s到每个顶点的最短路长度 d i s dis dis(初始化为 + ∞ +\infty +∞表示该点不可达,源点初始化为0),路径数组 p a t h path path(存储其上一个来源顶点,初始化为 − 1 -1 −1表示无路径)
- 属性: M i n Min Min
- 状态计算:设当前边为
<
u
,
v
>
<u,v>
<u,v>,循环执行下列步骤,直到没有结点的
d
i
s
dis
dis再更新或循环
V
−
1
V-1
V−1轮(若
V
−
1
V-1
V−1轮后还在更新说明存在负环)
- 不选第 k k k条边:若选当前边不能使边的终点 v v v到源点 s s s的距离减小,则不选该边。 d i s [ v ] dis[v] dis[v]不变。
- 选第 k k k条边:若选当前边能够使边的终点 v v v到源点 s s s的距离减小,则选该边。 d i s [ v ] = d i s [ u ] + w dis[v]=dis[u]+w dis[v]=dis[u]+w。(该边被松弛)
- 状态转移方程式:
d
i
s
[
v
]
=
m
i
n
(
d
i
s
[
v
]
,
d
i
s
[
u
]
+
w
)
dis[v]=min(dis[v],dis[u]+w)
dis[v]=min(dis[v],dis[u]+w)
实际上,此处默认运用了滚动数组压到了一维。
- 状态表示:
- 复杂度: O ( V E ) O(VE) O(VE)
- 与Dijkstra算法的区别:
- Dijkstra每次考查的是先前未被考察且 d i s dis dis最小的点,一旦被考查后其将不再被考查,因此不能处理负权图
- Bellman-Ford每次都直接对所有边进行遍历,每轮循环中所有顶点的 d i s dis dis都有可能被修改,且最坏情形下到 V − 1 V-1 V−1轮才确定,因此可适用于负权图
- Bellman-Ford的队列优化:SPFA
C++实现
int n,m,s;//点数 边数 源点
struct edge{
int f,t,w;
}e[MAXE];
void bellman(){
vector<int>dis(MAXV,INF),path(MAXV,-1);
dis[s]=0;
for(int k=0;k<n-1;k++)//进行V-1次循环
for(int i=0;i<m;i++)//遍历所有边
ll U=e[i].f,V=e[i].t,W=e[i].w;
if(dis[U]!=INF&&W!=INF&&dis[V]>dis[U]+W){
//松弛条件:源点dis非无穷,边权值非无穷,源点中转使终点距离更近
dis[V]=dis[U]+W;
path[V]=U;
}
}
路径输出
void print(int s,int t){
if(s==t){cout<<s<<' ';return;}
print(s,path[t]);
cout<<t<<' ';
}
判断负环
在循环结束后,加入判断是否有边 < u , v > <u,v> <u,v>满足 d i s [ v ] > d i s [ u ] + w dis[v]>dis[u]+w dis[v]>dis[u]+w,若有则表明还可继续松弛,存在负环。
bool check(){
for(int i=0;i<m;i++)
if(dis[e[i].t]>dis[e[i].f]+e[i].w) //若是非连通图,需再加条件dis[e[i].t]!=INF
return 1;
return 0;
}
负环还可用SPFA进行判断。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具