最短路问题 Bellman-Ford(单源最短路径)(图解)

这篇具有很好参考价值的文章主要介绍了最短路问题 Bellman-Ford(单源最短路径)(图解)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

核心思想:松弛操作

对于边(u,v),用dist(u)和(u,v)的和尝试更新dist(v):

                        dist(v) = min(dist(v) , dist(u)+l(u,v)

注:dist(i)为源点(起点)到i点的距离,l(u,v)为u->v的边权。

Bellman-Ford的基本操作是进行多次迭代,每一轮迭代对图上所有边进行松弛操作,直到再一次迭代中没有点的dist发生变化即可停止迭代。为什么呢?不妨假设已经没有dist发生变化了,再进行一轮迭代的话,很显然,之后的迭代没有产生任何作用,dist数组依旧没有改变,反倒增大了时间复杂度,这不是多此一举么。

图解:

                                                                ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​最短路问题 Bellman-Ford(单源最短路径)(图解)

初始:(S为源点)

初始设置为inf无穷大,表示还没有最短路

S A B C D E
0 inf inf inf inf inf

第一轮迭代:

对S点连出的边(s->e,s->a)

S A B C D E
0 7(0+7) inf inf inf 5(0+5)

 对A连出的边(a->c)

S A B C D E
0 7 inf 9(7+2) inf 5

对B连出的边(b->a)

S A B C D E
0 7 inf 9 inf 5

        dist(B)还没有找到最短路,更新其他点的最短路径无意义,故对B点的出边不进行松弛

对C连出的边(c->b)

S A B C D E
0 7 7(9+(-2)) 9 inf 5

对D连出的边(d->c,d->a)

S A B C D E
0 7 7 9 inf 5

         dist(D)还没有找到最短路,更新其他点的最短路径无意义,故对D点的出边不进行松弛 

 对E连出的边(e->d)

S A B C D E
0 7 7 9 6(5+1) 5

已经对所有的边进行了松弛操作,第一轮迭代结束

第二轮迭代

对S点连出的边(s->e,s->a)

S A B C D E
0 7 7 9 6 5

                                                                无需更新

 对A连出的边(a->c)

S A B C D E
0 7 7 9 6 5

                                                                无需更新 

对B连出的边(b->a)

S A B C D E
0 7 7 9 6 5

                                                              无需更新

对C连出的边(c->b)

S A B C D E
0 7 7 9 6 5

                                                               无需更新 

对D连出的边(d->c,d->a)

S A B C D E
0 2(6+(-4)) 7 5(6+(-1)) 6 5

 对E连出的边(e->d)

S A B C D E
0 2 7 5 6 5

已经对所有的边进行了松弛操作,第二轮迭代结束

第三轮迭代

与第一第二轮同理(此处直接给出迭代结束的结果)

S A B C D E
0 2 2 4 6 5

第四轮迭代

无任何更新,迭代结束,更新完成

算法分析:

如果最短路存在,一定存在一个不含环的最短路。(理由:对零环和正环,去掉后路径不会变长;对负环,若最短路径中存在负环,那一定不是最短路,负环可以无限绕下去,路径可以是负无穷)

最短路不含环,那么一条最短路径最多经过n-1个点(不含起点),所以最多需要n-1轮松弛操作。

复杂度分析:

最多进行n-1次迭代,每次迭代枚举遍历所有边,尝试通过边进行松弛操作,故复杂度为

O(N-1)*O(M)即O(NM),(注:N为点数,M为边数)

伪代码

   for (int i = 0; i <= n; i++)

        dist[i] = inf;//初始化为无穷大

    dist[s] = 0;//s为起点,自己到自己的最短路为0

    for (int k = 1; k <= n - 1; k++)//迭代n-1轮

    {

        for (int i = 1; i <= m; i++)//枚举每一条边

        {

            int x = u[i], y = v[i];

            if (dist[x] < inf)

                dist[y] = min(dist[y], dist[x] + w[i]);//松弛

        }

    }

 检查有无负环

将dist数组初始化为0,迭代n-1次后进行第n次迭代,如果第n次迭代有进行松弛操作,则一定存在负环,因为不存在负环最多只能进行n-1次松弛操作

代码实现:

void bellman_ford(int s, int end) // s为起点,end为终点

{

    memset(dis, 127, sizeof(dis));

    dis[s] = 0; //起点最短路为0

    pre[s] = -1;

    for (int i = 1; i <= n - 1; i++)

    {

        bool ok = false;

        for (int j = 1; j <= m; j++)

        {

            int x = edge[j].u, y = edge[j].v, w = edge[j].w;

            if (dis[x] < (1 << 30) && dis[x] + w < dis[y])

            {

                dis[y] = dis[x] + w;

                pre[y] = x; // y的上一个点为x,如不需打印路径无需pre数组

                ok = true;

            }

        }

        if (ok == false)

        {

            break; //未进行松弛操作,提前退出循环,减小时间复杂度

        }

    }

    if (dis[end] < (1 << 30))

        cout << dis[end] << "\n";

    else

        cout << "-1\n";

    // Print_Path(end); //打印路径

}

模板题 

题目链接:最短路 - 题目 - Daimayuan Online Judge

题目描述:

给你一张简单有向图,边权都为非负整数。以及一些询问,询问两个点之间的距离。

图用以下形式给出:

第一行输入三个整数 n,m,k表示图的顶点数、边数和询问次数,顶点编号从 1 到 n。

接下来 m 行,每行三个整数 x,y,z表示 x 到 y 有一条有向边,边权为 z。

接下来 k 行,每行两个整数 x,y 询问从 x 到 y 的最短路长度,如果无法到达,输出 −1。

输入格式:

第一行三个整数 n,m,k 表示图的顶点数、边数和询问次数。

接下来 m 行,每行有三个整数,代表一条边。

接下来 k 行,每行有两个整数,代表一次询问。

输出格式:

输出共 k 行,每行一个数表示一次询问的答案。

数据规模: 

对于所有数据,保证 2≤n≤5000,0≤m≤10000,1≤k≤5,1≤x,y≤n,x≠y,1≤z≤10000。

样例输入:

3 3 2

1 2 3

2 3 2

3 2 1

1 3

3 1

样例输出:

5

-1 

直接给代码了

#include <bits/stdc++.h>
using namespace std;
struct Edge
{
    int u, v, w;
} edge[100009];
int pre[100009];          //记录上一个点,为了打印最短路径
int dis[100009], n, m, k; // n为点数,m为边数,dis[i]为起点到i的最短距离
void Print_Path(int x)
{
    if (pre[x] == -1)
    {
        cout << x; //起点的pre为-1,所以x为起点
        return;
    }
    else
    {
        Print_Path(pre[x]);
        cout << "->" << x;
    }
}
void bellman_ford(int s, int end) // s为起点,end为终点
{
    memset(dis, 127, sizeof(dis));
    dis[s] = 0; //起点最短路为0
    pre[s] = -1;
    for (int i = 1; i <= n - 1; i++)
    {
        bool ok = false;
        for (int j = 1; j <= m; j++)
        {
            int x = edge[j].u, y = edge[j].v, w = edge[j].w;
            if (dis[x] < (1 << 30) && dis[x] + w < dis[y])
            {
                dis[y] = dis[x] + w;
                pre[y] = x; // y的上一个点为x
                ok = true;
            }
        }
        if (ok == false)
        {
            break; //未进行松弛操作,提前退出循环,减小时间复杂度
        }
    }
    if (dis[end] < (1 << 30))
        cout << dis[end] << "\n";
    else
        cout << "-1\n";
    // Print_Path(end); //打印路径
}
int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); //关同步流
    cin >> n >> m >> k;
    for (int i = 1; i <= m; i++) //读入边
    {
        cin >> edge[i].u >> edge[i].v >> edge[i].w;
    }
    for (int i = 1; i <= k; i++) // k次询问
    {
        int x, y;
        cin >> x >> y;
        bellman_ford(x, y);
    }
}

 参考文献:

《算法竞赛,入门经典(第二版)》

2022 Namomo Spring Camp Div2 Day8 直播课

ending

有什么错误之处欢迎指正!不胜感激!文章来源地址https://www.toymoban.com/news/detail-491285.html

到了这里,关于最短路问题 Bellman-Ford(单源最短路径)(图解)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 图论最短路径——Bellman-Ford Algorithm算法

    在图论中,寻找最短路径是一个常见且重要的问题。对于这个问题,有许多算法可以解决,其中最著名的是 Dijkstra 算法。然而,当图中包含负权边时,Dijkstra 算法可能无法正确工作。这时,贝尔曼-福特(Bellman-Ford)算法就派上了用场。 贝尔曼-福特算法可以在含有负权边的图

    2024年04月27日
    浏览(38)
  • 图论 - 最短路(Dijkstra、Bellman-Ford、SPFA、Floyd)

    单源:在边权正数时,稠密图用朴素Dijkstra,稀疏图用堆优化Dijkstra;存在负权边时,一般用SPFA,但是如果限制在k步内,则用Bellman-Ford。多源:只有Floyd,这个由于时间复杂度太高,在算法比赛中很少遇见。 1.问题描述 给定一个 n 个点 m 条边的有向图,图中可能存在重边和自

    2024年04月14日
    浏览(38)
  • Bellman-Ford-贝尔曼-福特-算法求最短路-负环

    Bellman-Ford(贝尔曼-福特)算法基于松弛操作的单源最短路算法。 e[u]存u点的出边的邻点和边权,d[u]存u点到源点的距离。 初始化,ds]=0,d[其它点]=+o; 执行多轮循环。每轮循环,对所有边都尝试进行一次松弛操作; 当一轮循环中没有成功的松弛操作时,算法停止 为什么最坏需要

    2024年02月13日
    浏览(38)
  • 最短路径算法( Dijkstra + Bellman-Ford + SPFA + Floyd)

       文章目录 一、Dijkstra 算法 1、1 朴素版Dijkstra算法 1、1、1 Dijkstra求最短路 I 1、1、2 题解关键思路与与解答 1、2 堆优化版Dijkstra算法 1、2、1 Dijkstra求最短路 II 1、2、2 题解关键思路与答案 二、Bellman-Ford 算法 2、1 Bellman-Ford算法求有边数限制的最短路 2、1、1 题目描述 2、

    2023年04月08日
    浏览(37)
  • 图论14-最短路径-Dijkstra算法+Bellman-Ford算法+Floyed算法

    https://github.com/Chufeng-Jiang/Graph-Theory/tree/main/src/Chapter11_Min_Path 2.4.1 判断某个顶点的连通性 2.4.2 求源点s到某个顶点的最短路径 存放节点编号和距离 这里的缺点就是,更新node时候,会重复添加节点相同的node,但是路径值不一样。不影响最后结果。 更新pre数组 输出路径 初始化两

    2024年02月04日
    浏览(36)
  • 【算法基础:搜索与图论】3.4 求最短路算法(Dijkstra&bellman-ford&spfa&Floyd)

    关于最短路可见:https://oi-wiki.org/graph/shortest-path/ 无向图 是一种 特殊的 有向图。(所以上面的知识地图上没有区分边有向还是无向) 关于存储:稠密图用邻接矩阵,稀疏图用邻接表。 朴素Dijkstra 和 堆优化Dijkstra算法的 选择就在于图 是 稠密的还是稀疏的。 算法步骤: 有一

    2024年02月16日
    浏览(45)
  • 【图论】单源最短路问题

    Dijkstra算法是一种单源最短路径算法,用于找出图中从一个源点到其他所有点的最短路径。该算法的原理是采用贪心策略,每次将距离源点最近的点加入到已确定最短路径的集合中,并更新其它节点的距离。具体实现过程如下: 初始化距离数组dist[],源点距离为0,其余点距离

    2024年02月13日
    浏览(40)
  • 单源最短路径问题——分支限界法(Java)

    1.1 分支限界法求解目标 分支限界法与回溯法的不同求解目标: 回溯法的求解目标:找出解空间树中满足约束条件的 所有解 ; 分支限界法的求解目标:找出满足约束条件的 一个解 ,或是在满足约束条件的解中找出使用某一目标函数值达到极大或极小的解,即在某种意义下

    2024年02月06日
    浏览(36)
  • 图论详解——Bellman-Ford(清晰易懂)

    开学第一周,晚上属实作业有点乱 于是就拖更了一周 今天我们来讲解一下图论最短路径算法中 最简单 最清晰易懂 同时时间复杂度最高的算法 它的时间复杂度能达到O(VE)(点的数量*边的数量) 在学习Bellman-Ford之前,你需要先学会链式前向星 大家可以上网或者其他途径自行

    2024年02月06日
    浏览(44)
  • Bellman-ford 贝尔曼-福特算法

    Bellman-ford算法可以解决负权图的单源最短路径问题 --- 它的优点是可以解决有负权边的单源最短路径问题, 而且可以判断是否负权回路 它也有明显的缺点,它的时间复杂度O(N*E)(N是点数 , E是边数)普遍是要高于Dijkstra算法O(N^2)的,像这里,我们使用邻接矩阵实现,那

    2024年02月06日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包