• AcWing 853. 有边数限制的最短路


    题目传送门

    一、(bellman-ford)算法

    算法思路

    for n次 //迭代n次(n个结点)
      for 所有边    //a,b,w 表示存在一条a->b 的边,权重是 w
         dist[b]=min(dist[b],dist[a]+w)
    

    和迪杰斯特拉算法基本思想是一致的:通过一个一个的引入其它结点,让两点之间的距离更短。数学上可以证明,执行完(n)次后,最终可以得到(asim b)的最短路径。

    二、要点总结

    • (bellman-ford)可以处理负权边,而(Dijkstra)只能处理正权边。

    • (bellman-ford)算法可以处理负权回路,因为它求得的最短路是有限制的,是限制了边数的,这样不会永久的走下去,会得到一个解;

    • 有负权回路的话,最短路是不一定存在的。为什么是不一定,可以看图(1)和图(2).

      图$1$描述了一直走下去一直小,那么,最短路径就是负无穷,不存在最短路径。
      图$2$描述了虽然有负权回路,但由于此负权回路不在$1$~$n$的路线中,不影响最短路,所以存在最短路径。
    • (bellman-ford)是可以找负环的,但时间复杂度比较高,一般不用它来找负环。(bellman-ford)能干的事,(SPFA)都能干,而且时间复杂度比(bellman-ford)要低。

    • (SPFA)算法各方面优于该算法,但是在碰到限制了最短路径长度时就只能用(bellman-ford)了,此时直接把(n)重循环改成(k)次循环即可

    • 有边数限制的最短路径
      (bellman-ford)算法擅长解决有边数限制的最短路问题,其它算法没有这个功能!
      比个现实中的场景:旅游(n)个城市,(1)->(n)没有直达的飞机,需要经其它的城市进行中转,票价就是中转的机票票价之和,每次中转都会让人的心情不爽一点,也就是不能超过(k)次中转。这个场景因为
      限制了边数的数量上限(k),就只能用(bellman-ford)算法。

    三、C++ 代码

    #include <bits/stdc++.h>
    
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int N = 510;   //结点数
    const int M = 10010; //边数
    
    //存边的方式
    struct Edge {
        //三个整数a,b,c,表示存在一条从点a到点b的有向边,边长为c。
        int a, b, c;
    } edges[M]; //边的数组
    
    int n, m; //n个结点,m条边
    int k;    //表示从1号点到n号点的最多经过k条边的最短距离
    int dist[N];//距离数组
    int backup[N];//备份数组
    
    // 输出一个整数,表示从1号点到n号点的最多经过k条边的最短距离
    void bellman_ford() {
        // 初始化为无穷大
        memset(dist, 0x3f, sizeof dist);
        dist[1] = 0; //1点节点到自己的长度是0
    
        //循环k次,因为只有k次机会
        for (int i = 1; i <= k; i++) {
            //上一次的结果(备份出来)
            //为了在一次迭代中防止更新多条边的情况,需要备份出来
            memcpy(backup, dist, sizeof dist);//防止发生串联,只用上一次迭代的结果就不会发生串联。
            //循环所有边
            for (int j = 0; j < m; j++) {
                Edge e = edges[j];
                dist[e.b] = min(dist[e.b], backup[e.a] + e.c);
            }
        }
    }
    
    int main() {
        //读入优化
        ios::sync_with_stdio(false);
        //读入数据
        cin >> n >> m >> k;
        for (int i = 0; i < m; i++) {
            //三个整数a,b,c,表示存在一条从点a到点b的有向边,边长为c。
            int a, b, c;
            cin >> a >> b >> c;
            edges[i] = {a, b, c};
        }
        //调用bellman_Ford算法
        bellman_ford();
        //输出结果
        if (dist[n] > INF / 2) puts("impossible");
        else printf("%d
    ", dist[n]);
        return 0;
    }
    
  • 相关阅读:
    一起谈.NET技术,JAVA与.NET的相互调用——TCP/IP相互调用基本架构 狼人:
    一起谈.NET技术,.NET异步编程:IO完成端口与BeginRead 狼人:
    一起谈.NET技术,VS2010自定义新建文件模版 狼人:
    一起谈.NET技术,理解.NET程序集的执行过程 狼人:
    一起谈.NET技术,JAVA与.NET的相互调用——利用JNBridge桥接模式实现远程通讯 狼人:
    一起谈.NET技术,用NuGet掌管你的Visual Studio扩展 狼人:
    一起谈.NET技术,Visual Studio 2010 中的代码约定设置 狼人:
    一起谈.NET技术,C#中的语言特性都是从何而来? 狼人:
    一起谈.NET技术,关于Winform下,获取Treeview中CheckBox选中项的技巧 狼人:
    一起谈.NET技术,ASP.NET MVC 3和Razor中的@helper 语法 狼人:
  • 原文地址:https://www.cnblogs.com/littlehb/p/15319764.html
Copyright © 2020-2023  润新知