(算法基础)Bellman-ford算法

适用情景

  1. Bellman-ford算法一般运用在求最短路的问题当中,适用于求最短路问题的单源最短路的存在负权边的情况。


时间复杂度

  1. 时间复杂度为O(m*n),n表示有n个点,m表示有m条边。


算法解释

  1. 首先是循环(迭代)n次,当然实际情况也不一定要求是n次,也可以是k次。但是这里面的实际意义非常关键:如果说该算法循环k-1次,从1号点经过k-1条边能够到达的所有点你们的最短距离已经确定下来;如果说该算法循环k次,从1号点经过k条边能够到达的所有点你们的最短距离也已经确定下来........ (当然,这个最短距离并不是最优版,是在只经过<=k条边的情况下的最短距离)

  1. 在每一次循环里面,去遍历所有的边(也正是因为如此,所以说这个算法里面存边的方式可以很随意,因为我只需要保证所有的边都能够全部遍历到就可以,因此可以直接开一个结构体去存边就行)。

struct point    //存边(a,b,c)  表示从点a指向点b,权重为c
{
    int x;
    int y;
    int z;
}
  1. 接上文,比如说对于每一条边a,b,c(表示从点a指向点b,权重为c),那么这时候就干这个操作:

dist[b]=min(dist[b],dist[a]+c)   //先不考虑备份,大意先搞懂
  1. 在刚开始创建dist数组的时候,默认所有的点到一号点的距离都是无穷大,当然一号点本身除外。

memset(dist,0x3f,sizeof(dist));
dist[1]=0;
  1. 在进行每一次循环迭代的时候。需要进行一个备份操作,把dist数组先备份到backup数组里面,这主要是为了防止串联现象的发生。如图解:

没有开始循环的时候,dist[ 1 ]=0, dist [ 2 ]与dist[ 3 ]都是正无穷大。然后进行了一次循环/迭代,这时候由于2号点与3号点都是可以通过1号点只经过一条边就可以走到,因此二号点和三号点的最短路已经确定,它们的最短距离分别为1和3。然后我们根据这个算法,在每一次循环的时候去遍了所有的边(3条),每次都dist[ b ] = dist[ a ] + c ; 如果没有备份的话,会导致dist[ 3 ]最终变为2,虽然2确实是更加优化的最短距离,但是这个最短距离是在从1号点经过两条边的基础之上,而我现在只进行了一次循环,我所得到的是从1号点只经过1条边走到的所有点的在最短路中边数为1这个前提下的最短距离。

for (int i=0;i<k;i++)
{
    memcpy(backup,dist,sizeof(dist));
    for (int j=1;j<=m;j++)
    {
        dist[b]=backup[a]+c;
    }
}
  1. 最后万一如果说从1号点是走不到n号点的,这种情况该如何在循环结束后去判断呢?因为dist数组所有元素初始化都是无穷大0x3f3f3f3f,但这边有个小坑,不能直接等于这个数字去判断。因为我们知道每次循环的话都会去遍历所有的边,在这个过程当中如果说n号点与某一个点之间存在着负权边,那么会对这个无穷大的数字造成略微的缩小。因此要这样子

if (dist[n]>0x3f3f3f3f/2)
{
    printf("impossible\n");
}
.........

例题

来源:AcWing

853. 有边数限制的最短路 - AcWing题库

#include <stdio.h>
#include <string.h>
#define MIN(a,b) ((a)<(b)?(a):(b))
typedef struct point 
{
    int a;
    int b;
    int c;
}point;
int main()
{
    int n,m,k,a,b,c=0;
    scanf("%d %d %d",&n,&m,&k);
    point arr[m+1];
    int dist[n+1];
    memset(dist,0x3f,sizeof(dist));
    dist[1]=0;
    int backup[n+1];
    for (int i=1;i<=m;i++)
    {
        scanf("%d %d %d",&a,&b,&c);
        arr[i].a=a;
        arr[i].b=b;
        arr[i].c=c;
    }
    for (int i=0;i<k;i++)
    {
        memcpy(backup,dist,sizeof(dist));
        for (int j=1;j<=m;j++)
        {
            dist[arr[j].b]=MIN(dist[arr[j].b],backup[arr[j].a]+arr[j].c);
        }
    }
    if (dist[n]>0x3f3f3f3f/2)
    {
        printf("impossible\n");
    }
    else
    {
        printf("%d\n",dist[n]);
    }
    return 0;
}

补充

  1. 一般来说bellman-ford算法能够搞定的,SPFA算法都能够搞定,那是不是意味着这个算法就没有存在的必要了呢?并不是的,这个bellman-ford算法他的与众不同之处在于可以针对有边数限制的最短路问题。因为他的外循环k次,表示从一号点经过k条边所能够到达的所有点的最短路距离已经确定下来(这个最短路距离并不是真正最优版,有个前提是在这个最短路途当中,边数是小于等于k)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Dijkstra算法Bellman-Ford算法都是用于解决图中单源最短路径问题的经典算法。 Dijkstra算法是一种贪心算法,用于求解从给定源节点到其他所有节点的最短路径。算法通过维护一个优先队列(或最小堆)来选择当前距离源节点最近的节点,并逐步扩展路径长度最短的节点。具体步骤包括:初始化源节点的距离为0,将其加入优先队列;从队列中取出距离最小的节点,并对其相邻节点进行松弛操作,更新其距离;重复上述步骤直到队列为空。 Bellman-Ford算法是一种动态规划算法,可以处理带有负权边的图。算法通过对所有边进行V-1轮松弛操作来逐步求解最短路径。具体步骤包括:初始化源节点距离为0,其他节点距离为正无穷;迭代V-1轮,对所有边进行松弛操作,即尝试通过更新边权值来缩短源节点到其他节点的距离;检测是否存在负权回路,如果存在则说明图中存在无限负权路径。 两者的主要区别在于: - Dijkstra算法要求图中边权值非负,而Bellman-Ford算法可以处理带负权边的情况。 - Dijkstra算法时间复杂度为O((V + E)logV),其中V为节点数量,E为边数量;而Bellman-Ford算法时间复杂度为O(VE),在稀疏图中效率较低。 选择使用哪种算法取决于具体的问题场景和图的特性。如果图中不存在负权边,且需要求解单源最短路径,Dijkstra算法是一个较好的选择。而如果图中可能存在负权边,并且需要检测负权回路,或者只需求解单源最短路径且图较稠密,可以考虑使用Bellman-Ford算法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

絕知此事要躬行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值