zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & ManacherK
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher J
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher I
    pat 1065 A+B and C (64bit)(20 分)(大数, Java)
    pat 1069 The Black Hole of Numbers(20 分)
    pat 1077 Kuchiguse(20 分) (字典树)
    pat 1084 Broken Keyboard(20 分)
    pat 1092 To Buy or Not to Buy(20 分)
    pat 1046 Shortest Distance(20 分) (线段树)
    pat 1042 Shuffling Machine(20 分)
  • 原文地址:https://www.cnblogs.com/littlehb/p/15319764.html
Copyright © 2011-2022 走看看