zoukankan      html  css  js  c++  java
  • Bellman-ford 算法详解

    昨天说的dijkstra固然很好用,但是却解决不了负权边,想要解决这个问题,就要用到Bellman-ford.

    我个人认为Bellman-Ford比dijkstra要好理解一些,还是先上数据(有向图):

    5 7
    1 2 8
    1 3 5
    2 3 -6
    5 4 -3 2 4 7 3 5 -2 4 5 -3

    在讲述开,先设几个数组:

    origin[i]表示编号为i这条边的起点编号,如origin[4]=2

    destination[i]表示编号为i这条边的终点编号,如origin[5]=5

    value[i]表示编号为i这条边的权值,如value[3]=-6

    dis[i],和昨天一样,源点到i号点的估计距离,经过不断更新会变成时机距离,就是答案。

    bellmanford的实际意义就是扫描一条边,看如果走这条边能不能使这条边的dis[destination[i]],变少,现在我来模拟一下:

    初始的dis:[0,∞,∞,∞,∞]

    首先从第一条边1 2 8开始,判断走这条边能不能使这条边的终点的dis变短,原本dis[2]=∞,而dis[1]=0,而这条边的权值:value[1]=8,0+8<∞所以将dis[2]更新成8.

    dis[0,8,∞,∞,∞]

    然后是第二条边,用刚才的方法将dis[3]从∞更新成5.

    dis[0,8,5,∞,∞]

    第三条2 3 -8,原本的dis[3]=5,如果走第三条边,则dis[3]=dis[2]+value[3]=8+(-6)=2<5,所以dis[3]更新成2.

    dis[0,8,2,∞,∞]

    以此类推,经过第一轮更新,dis数组如下:

    dis[0,8,2,15,0]

    但是第一次更新后,并不是最优解于是开始第二次更新。

    按照第一次更新的步骤一步一步来得到的答案是

    dis[0,8,2,-3,0]

    这便是最优解,但是问题来了,一般要更新多少次呢?

    n-1次。这样能保证更新出的一定是最优解。

    好了,呈上代码:

    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    using namespace std;
    int dis[10010];
    int origin[10010],destination[10010],value[10010];//刚刚说过的三个数组
    int n,m;
    void Bellman_ford(int a)
    {
        memset(dis,88,sizeof(dis));//赋初始值
        dis[a]=0;
        for(int i=1;i<=n-1;i++)//更新n-1次
            for(int j=1;j<=m;j++)//更新每一条边
                dis[destination[j]]=min(dis[destination[j]],dis[origin[j]]+value[j]);//判断是否更新
     } 
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=m;i++)
            cin>>origin[i]>>destination[i]>>value[i];
        Bellman_ford(1);
        for(int i=1;i<=n;i++)
            cout<<dis[i]<<" "; 
    }

     有些人可能发现了,很多时候实际上不用更新n-1次,因此我们可以用队列优化:

    每次选出队首点,对与队首点链接的所有点的dis进行更新,并加入队列,然后队首点pop出队列,

    这个算法最好用邻接表实现,代码如下:

    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <queue>
    using namespace std;
    int dis[10010];
    int book[10010];
    int origin[10010],destination[10010],value[10010];
    int n,m;
    int total;
    int next[10010],head[10010];
    void adl(int a,int b,int c)//邻接表
    {
       total++;
       origin[total]=a;
       destination[total]=b;
       value[total]=c;
       next[total]=head[a];
       head[a]=total;
    }
    void Bellman_ford(int a)
    {
        memset(book,0,sizeof(book));//book[i]表示i号点是否在队列里
        memset(dis,88,sizeof(dis));
        queue <int> q;
        q.push(a);
        book[a]=1;
        dis[a]=0;
        while(!q.empty())//当队列不为空时更新
        {
            for(int e=head[q.front()];e;e=next[e])//枚举队首点相邻的每一个点
            {
                if(dis[destination[e]]>dis[origin[e]]+value[e])
                {
                    dis[destination[e]]=dis[origin[e]]+value[e];
                    if(book[destination[e]]==0)
                    {
                        q.push(destination[e]);//将更新的这一个点入队
                        book[destination[e]]=1;
                    }
                }
            }
            q.pop();//弹出队首元素
        }
     } 
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=m;i++)
        {
            int a,b,c;
            cin>>a>>b>>c;
            adl(a,b,c);
       } 
        Bellman_ford(1);
        for(int i=1;i<=n;i++)
            cout<<dis[i]<<" "; 
    }

    总结一下,bellman_ford的空间复杂度是m时间复杂度是O(nm),经过队列优化,时间复杂度是<=O(nm)。

  • 相关阅读:
    excel使用总结
    使用spring的@Scheduled注解执行定时任务,启动项目不输出警告
    web.xml配置错误导致applicationContext.xml配置重复加载
    POI3.8解决导出大数据量excel文件时内存溢出的问题
    linux常用命令总结
    javax.mail 发送邮件异常
    eclipse下创建maven项目
    java单例模式的几种写法比较
    msql数据迁移,myisam及innoDB
    JS监听回车事件
  • 原文地址:https://www.cnblogs.com/jason2003/p/7224580.html
Copyright © 2011-2022 走看看