zoukankan      html  css  js  c++  java
  • 最短路之Bellman-Ford算法

    说明:

    Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的。

    这时候,就需要使用其他的算法来求解最短路径,Bellman-Ford算法就是其中最常用的一个。

    适用条件&范围:

    单源最短路径(从源点s到其它所有顶点v);

    有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图);

    边权可正可负(如有负权回路输出错误提示);

    思想:

      我们规定节点都有一个key值,key值记录的是开始节点到本节点的最小距离,每个节点也都有一个p指针指向他的前驱节点。这里我们规定一个操作叫做松弛操作,我们的算法也是最终基于这个操作的。松弛操作就是去更新key的值。

            节点B的key值为8,表示从开始节点到B节点之前的最短估计距离是8,而节点A的key值3,是说从开始节点到A节点最短估计是3,当我们发现这个边时,从A到B的距离比较近,所以我们去更新B的key值,同时把B的前驱节点设置成A。这个过程就是松弛操作。

      我们说的Bellman-Ford算法是最简单的算法,就是从开始节点开始循环每一条边,对他进行松弛操作。最后得到的路径就是最短路径。过程如图:

    算法步骤:

    1.初始化:将除源点外的所有顶点的最短距离估计值 d[v] ← +∞, d[s] ←0;
    2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
    3.检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。

    代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 0x3f3f3f3f
    #define N 1010
    int nodenum, edgenum, original; //点,边,起点
    typedef struct Edge //
    {
        int u, v;
        int cost;
    } Edge;
    Edge edge[N];
    int dis[N], pre[N];
    bool Bellman_Ford()
    {
        int ok;
        for(int i = 1; i <= nodenum; ++i) //初始化,起点本身赋值为0,其余赋值为最大
            dis[i] = (i == original ? 0 : MAX);
        for(int i = 1; i <= nodenum - 1; ++i)
        {
            ok=1;
            for(int j = 1; j <= edgenum; ++j)
                if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) //松弛(顺序一定不能反)
                {
                    dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;
                    pre[edge[j].v] = edge[j].u;//这里用来存储路径
                    ok=0;
                }
            if(ok==1) //优化这里,如果这趟没跟新任何节点就可以直接退出了。
                break;
        }
        bool flag = 1; //判断是否含有负权回路
        for(int i = 1; i <= edgenum; ++i)
            if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost)
            {
                flag = 0;
                break;
            }
        return flag;
    }
    
    void print_path(int root) //打印最短路的路径(反向)
    {
        while(root != pre[root]) //前驱
        {
            printf("%d-->", root);
            root = pre[root];
        }
        if(root == pre[root])
            printf("%d
    ", root);
    }
    
    int main()
    {
        scanf("%d%d%d", &nodenum, &edgenum, &original);//输入点边起点,一般起点规定为1
        pre[original] = original;//为了输出最短路用的,前驱为本身
        for(int i = 1; i <= edgenum; ++i)
        {
            scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost);//有向图
        }
        if(Bellman_Ford())//如果没有负权
            for(int i = 1; i <= nodenum; ++i) //每个点最短路
            {
                printf("%d
    ", dis[i]);
                printf("Path:");
                print_path(i);
            }
        else
            printf("have negative circle
    ");
        return 0;
    }
  • 相关阅读:
    操作MS SQL Server 存储过程的类(外加ASP.NET MessageBox类)
    利用DataGrid的超级联接传值
    操作数据库系统信息
    鼠标指向表格中的一行时,该行背景色改变;点击行时,突出显示标记颜色
    asp.net下的UBB代码[C#]
    java 为什么要序列化
    oracle调用java方法的例子(下面所有代码都是在sql/plus
    oracle存储过程
    一个Java程序员应该掌握的10项技能
    七款天气预报代码
  • 原文地址:https://www.cnblogs.com/aiguona/p/7226533.html
Copyright © 2011-2022 走看看