目录

Bellman_ford 算法

94. 城市间货物运输 I

Bellman_ford 队列优化算法(又名SPFA)


学习链接:代码随想录

Bellman_ford 算法

94. 城市间货物运输 I

题目描述

某国为促进城市间经济交流,决定对货物运输提供补贴。共有 n 个编号为 1 到 n 的城市,通过道路网络连接,网络中的道路仅允许从某个城市单向通行到另一个城市,不能反向通行。

网络中的道路都有各自的运输成本和政府补贴,道路的权值计算方式为:运输成本 - 政府补贴。权值为正表示扣除了政府补贴后运输货物仍需支付的费用;权值为负则表示政府的补贴超过了支出的运输成本,实际表现为运输过程中还能赚取一定的收益。

请找出从城市 1 到城市 n 的所有可能路径中,综合政府补贴后的最低运输成本。如果最低运输成本是一个负数,它表示在遵循最优路径的情况下,运输过程中反而能够实现盈利。

城市 1 到城市 n 之间可能会出现没有路径的情况,同时保证道路网络中不存在任何负权回路。

输入描述

第一行包含两个正整数,第一个正整数 n 表示该国一共有 n 个城市,第二个整数 m 表示这些城市中共有 m 条道路。 

接下来为 m 行,每行包括三个整数,s、t 和 v,表示 s 号城市运输货物到达 t 号城市,道路权值为 v (单向图)。

输出描述

如果能够从城市 1 到连通到城市 n, 请输出一个整数,表示运输成本。如果该整数是负数,则表示实现了盈利。如果从城市 1 没有路径可达城市 n,请输出 "unconnected"。

        本题依然是单源最短路问题,求 从 节点1 到节点n 的最小费用。 但本题不同之处在于边的权值是有负数了

        从 节点1 到节点n 的最小费用也可以是负数,费用如果是负数则表示运输的过程中政府补贴大于运输成本。

        在求单源最短路的方法中,使用dijkstra 的话,则要求图中边的权值都为正数。

        本题是经典的带负权值的单源最短路问题,此时就轮到Bellman_ford登场了,接下来我们来详细介绍Bellman_ford 算法 如何解决这类问题。

        Bellman_ford算法的核心思想是 对所有边进行松弛n-1次操作(n为节点数量),从而求得目标最短路。

        如果 通过 A 到 B 这条边可以获得更短的到达B节点的路径,即如果 minDist[B] > minDist[A] + value,那么我们就更新 minDist[B] = minDist[A] + value ,这个过程就叫做 “松弛” 。

        其实 Bellman_ford算法 也是采用了动态规划的思想,即:将一个问题分解成多个决策阶段,通过状态之间的递归关系最后计算出全局最优解。

为什么是 n - 1次 松弛呢

        对所有边松弛一次,相当于计算 起点到达 与起点一条边相连的节点 的最短距离

        节点数量为n,那么起点到终点,最多是 n-1 条边相连。

        那么无论图是什么样的,边是什么样的顺序,我们对所有边松弛 n-1 次 就一定能得到 起点到达 终点的最短距离。

共有两个关键点。

        “松弛”究竟是个啥?

        为什么要对所有边松弛 n - 1 次 (n为节点个数) ?

那么Bellman_ford的解题过程其实就是对所有边松弛 n-1 次,然后得出起点到终点的最短路径。

松弛 n 次,松弛 n + 1次,松弛 2 * n 次会怎么样?

      其实没啥影响,结果不会变的,因为 题目中说了 “同时保证道路网络中不存在任何负权回路” 也就是图中没有 负权回路(在有向图中出现有向环 且环的总权值为负数)。

  本题代码

public class Main {

    // Define an inner class Edge
    static class Edge {
        int from;
        int to;
        int val;
        public Edge(int from, int to, int val) {
            this.from = from;
            this.to = to;
            this.val = val;
        }
    }

    public static void main(String[] args) {
        // Input processing
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        List<Edge> edges = new ArrayList<>();

        for (int i = 0; i < m; i++) {
            int from = sc.nextInt();
            int to = sc.nextInt();
            int val = sc.nextInt();
            edges.add(new Edge(from, to, val));
        }

        // Represents the minimum distance from the current node to the original node
        int[] minDist = new int[n + 1];

        // Initialize the minDist array
        Arrays.fill(minDist, Integer.MAX_VALUE);
        minDist[1] = 0;

        // Starts the loop to relax all edges n - 1 times to update minDist array
        for (int i = 1; i < n; i++) {

            for (Edge edge : edges) {
                // Updates the minDist array
                if (minDist[edge.from] != Integer.MAX_VALUE && (minDist[edge.from] + edge.val) < minDist[edge.to]) {
                    minDist[edge.to] = minDist[edge.from] + edge.val;
                }
            }
        }

        // Outcome printing
        if (minDist[n] == Integer.MAX_VALUE) {
            System.out.println("unconnected");
        } else {
            System.out.println(minDist[n]);
        }
    }
}

Bellman_ford 队列优化算法(又名SPFA)

         Bellman_ford 算法 每次都是对所有边进行松弛,其实是多做了一些无用功。

        只需要对 上一次松弛的时候更新过的节点作为出发节点所连接的边 进行松弛就够了

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐