zoukankan      html  css  js  c++  java
  • Bellman-Ford 算法

    Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的。这时候,就需要使用其他的算法来求解最短路径,Bellman-Ford算法就是其中最常用的一个。

    1、Bellman-Ford算法的流程如下:

    ①数组dist[i]记录从源点s到顶点i的路径长度,初始化数组dist[n]为INF, dist[s]为0;

    ②以下操作循环执行至多n-1次,n为顶点数:
     对于每一条边e(u, v),如果dist[u] + w(u, v) < dist[v],则另dist[v] = dist[u]+w(u, v)。w(u, v)为边e(u,v)的 权值;

    ③若上述操作没有对dist进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;

    ④为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在dist[u] + w(u, v) < dist[v]的边,则图中存在负环路,即是说该图无法求出单源最短路径。否则数组dist[n]中记录的就是源点s到各顶点的最短路径长度。

    2、参考代码

    /**
    该代码修改自《挑战程序设计》第二版
    */
    #include <iostream>
    #include <string.h>
    #include <stack>
    #define INF 1000000000
    #define MAX_E 99
    #define MAX_V 100
    using namespace std;
    struct edge{
    int  from,to,cost;//边的起点,终点,权值
    };
    edge es[MAX_E];
    int d[MAX_V];//源点到顶点的最短距离
    int path[MAX_V];//存最短路径
    int V,E;//顶点数,边数
    int s;//源点
    void BellmanFord(int s);
    bool find_negative_loop();//判断是否有负环
    void Print();
    int main()
    {
        cout << "请输入图的顶点数,边数,源点:";
        cin >>V>>E>>s;
    
        cout << "请输入" <<E<< "条边的起点、终点以及权值:
    ";
        for (int i = 0; i <E; i++)
            cin >>es[i].from>> es[i].to >> es[i].cost;
        if(!find_negative_loop()){
            BellmanFord(s);
            Print();
        }else{
        cout<<"有负环"<<endl;
        }
    
        return 0;
    }
    void BellmanFord(int s){
        for(int i=0;i<V;i++){
            d[i]=INF;
        }
        d[s]=0;
        while(true){//最多循环V-1次
            bool update=false;
            for(int i=0;i<E;i++){
                edge e=es[i];
                if(d[e.from]!=INF&&d[e.to]>d[e.from]+e.cost){
                    d[e.to]=d[e.from]+e.cost;
                    path[e.to]=e.from;
                    update=true;
                }
            }
            if(!update)//如果没有边的距离更新说明已经找完了
                break;
        }
    
    }
    bool find_negative_loop(){
        memset(d,0,sizeof(d));
        for(int i=0;i<V;i++){
            for(int j=0;j<E;j++){
                edge e=es[j];
            if(d[e.to]>d[e.from]+e.cost){
                d[e.to]=d[e.from]+e.cost;
                if(i==  V-1)return true;//如果其在第V次仍然更新则说明其有负环
                ///因为最短路径不可能经过同一个点两次,所以最多循环V-1次
                ///但如果有负环会无限循环下去,自己可以举个栗子试一下
            }
            }
        }
        return false;
    
    }
    
    void Print()
    {
        for (int i = 0; i < V; i++)
        {
            if (i != s)
            {
                int p = i;
                stack<int> st;
                cout << "顶点 " << s << " 到顶点 " << p << " 的最短路径是: ";
    
                while (s != p)  //路径顺序是逆向的,所以先保存到栈
                {
                    st.push(p);
                    p = path[p];
                }
    
                cout << s;
                while (!st.empty())  //依次从栈中取出的才是正序路径
                {
                    cout << "--" << st.top();
                    st.pop();
                }
                cout << "    最短路径长度是:" << d[i] << endl;
            }
    
        }
    }
    祝你早日攒够失望,然后开始新的生活。
  • 相关阅读:
    Hive 优化汇总
    PostgreSQL (简称gp)小集
    yarn 日志查看
    Python: 对CSV文件读写 和 Md5加密
    Linux查找命令find、locate、whereis、which、type
    EChars文档
    php中定时计划任务的实现原理
    如何启动、关闭和设置ubuntu防火墙
    MySQL中的insert ignore into, replace into等的一些用法总结
    Linux三剑客之老二-------sed命令详解
  • 原文地址:https://www.cnblogs.com/LuRenJiang/p/7278458.html
Copyright © 2011-2022 走看看