最短路算法入门(Bellman-Ford算法)
前置:
对于无向图中的一条边(u↔v),可以看作有向图中两条边(u→v)and(v→u)的结合。
我们可以通过这种方式把无向图转化为有向图,
从而使用有向图的最短路算法。
图的一些记号:
G = <V,E>代表一个简单有向图(简单图:没有重边和自环)
n = |V|代表顶点数
m = |E|代表边数
l(u,v)代表u到v的边权
S代表起点,T代表终点
dist(u)代表当前求出的S到u的最短路径长度
特别地:
对于所有边权都大于等于0的图,
任意两个顶点之间的最短路,
显然不会经过重复的顶点或者边。
也就是说任意一条最短路经过的顶点数不会超过n个,边不会超过n-1条。
而对于有边权为负的图,有可能图中会存在负环,此时途径负环的最短路没有意义。
Bellman-Ford算法
核心思想:
松弛操作(Relieve),即对于边(u,v),用dist(u)和l(u,v)的和 尝试更新dist(v):
dist[v] = min(dist[v], dist[u]+ l(u,v))
如此,不断尝试对图上的每一条边进行松弛操作。
每次迭代就对所有的边进行该操作,直到某一次迭代没有发生任何改变时停止。
在最短路存在的情况下,由于一次迭代会使得最短路的边数至少+1
而S到每个顶点的最短路经过的边数最多为n-1
因此整个算法最多会进行n-1轮迭代,每一轮复杂度为O(m)
则总复杂度为O(nm)
基于此我们得出,当从S出发能到达一个负环时,就会进行n轮以上的迭代
我们可以外加一个超级初节点,使得其到任意一个点的边权为0,
从而在O(nm)的复杂度里检测图上是否存在负环。
代码如下:
1 struct Edge{
2 int x, y, v;
3 } edge[M + 1];
4
5 int n, m, dist[N + 1], pre[N + 1];
6
7 int shortestpath(int s, int t){
8 memset(dist, 127, sizeof(dist)); //Initialization
9 dist[s] = 0; //Initialization
10 for (;;){
11 //在每次迭代中,遍历所有边,尝试用Relieve_Operation更新距离数组
12 bool ok = false;
13 for (int i = 1;i <= m;i++){
14 int x = edge[i].x;
15 int y = edge[i].y;
16 int v = edge[i].v;
17 if (dist[x] + 1 << 30){
18 if (dist[x] + v < dist[y]){
19 dist[y] = dist[x] + v;
20 pre[y] = x;
21 ok = true;
22 }
23 }
24 }
25 if (!ok){
26 break;
27 }
28 }
29 return dist[t];
30 }
31 //pre数组是为了打印路径
32 //此代码保证了题目的数据不会出现负环
大名鼎鼎的SPFA就是Bellman-Ford的队列优化方法,但是不要写就对了,这里也直接略过。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人