代码随想录算法训练营第六十一天| 96. 城市间货物运输 III (Bellman_Ford 单源限制节点最短路径算法)、97. 小明逛公园 (Floyd 算法)

[KamaCoder] 96. 城市间货物运输 III

[KamaCoder] 96. 城市间货物运输 III 文章解释

题目描述

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

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

请计算在最多经过 k 个城市的条件下,从城市 src 到城市 dst 的最低运输成本。

输入描述

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

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

最后一行包含三个正整数,src、dst、和 k,src 和 dst 为城市编号,从 src 到 dst 经过的城市数量限制。

输出描述

输出一个整数,表示从城市 src 到城市 dst 的最低运输成本,如果无法在给定经过城市数量限制下找到从 src 到 dst 的路径,则输出 "unreachable",表示不存在符合条件的运输方案。

输入示例
6 7
1 2 1
2 4 -3
2 5 2
1 3 5
3 5 1
4 6 4
5 6 -2
2 6 1
输出示例
0
提示信息

从 2 -> 5 -> 6 中转一站,运输成本为 0。 

1 <= n <= 1000; 

1 <= m <= 10000; 

-100 <= v <= 100;

[KamaCoder] 96. 城市间货物运输 III (Bellman_Ford 单源限制节点最短路径算法)

自己看到题目的第一想法

    无

看完代码随想录之后的想法

    嗯... 很复杂... 复杂到脑子不知道怎么计算了... 不过比起 Floyd 还是直观一些.

    核心的思想就是, 如果每次只展开和出发站点最近的边, 那么经过展开 k + 1 条边(k 为允许经过的车站数量), 就能计算出从出发站点到目的站点的最短距离. 我个人认为, 目前的算法是允许在负权回路里转圈圈的.

   

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;

public class Main {
    
    private static LinkedList<Edge>[] nodes = null;
    private static int[] minDist = null;
    private static int[] minDistCopy = null;

    public static void main(String[] args) {
        
        Scanner scanner = new Scanner(System.in);
        
        int nodeCount = 0;
        int sideCount = 0;
        
        nodeCount = scanner.nextInt();
        sideCount = scanner.nextInt();
        
        minDist = new int[nodeCount + 1];
        minDistCopy = new int[nodeCount + 1];
        Arrays.fill(minDist, Integer.MAX_VALUE);
        
        nodes = new LinkedList[nodeCount + 1];
        for (int i = 1; i <= nodeCount; i++) {
            nodes[i] = new LinkedList();
        }
        
        for (int i = 0; i < sideCount; i++) {
            Edge edge = new Edge(scanner.nextInt(), 
                scanner.nextInt(), scanner.nextInt());
            nodes[edge.from].add(edge);
        }
        
        int start = scanner.nextInt();
        int end = scanner.nextInt();
        int limit = scanner.nextInt();
        
        minDist[start] = 0;
        
        // 松弛 limit + 1 次
        for (int i = 0; i <= limit; i++) {
            System.arraycopy(minDist, 0, minDistCopy, 0, minDistCopy.length);
            for (int j = 1; j < nodes.length; j++) {
                for (Edge edge : nodes[j]) {
                    if (minDistCopy[edge.from] != Integer.MAX_VALUE 
                        && minDistCopy[edge.from] + edge.value < minDist[edge.to]) {
                        minDist[edge.to] = minDistCopy[edge.from] + edge.value;
                    }
                }
            }
        }
        
        if (minDist[end] == Integer.MAX_VALUE) {
            System.out.println("unreachable");
        } else {
            System.out.println(minDist[end]);
        }
    }
    
    private static class Edge {
        
        int from;
        int to;
        int value;
        
        public Edge(int from, int to, int value) {
            this.from = from;
            this.to = to;
            this.value = value;
        }
    }
}
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;

// Floyd 算法
public class Main {
    
    private static LinkedList<Edge>[] nodes = null;
    private static int[] minDist = null;
    private static int[] minDistCopy = null;

    public static void main(String[] args) {
        
        Scanner scanner = new Scanner(System.in);
        
        int nodeCount = 0;
        int sideCount = 0;
        
        nodeCount = scanner.nextInt();
        sideCount = scanner.nextInt();
        
        minDist = new int[nodeCount + 1];
        minDistCopy = new int[nodeCount + 1];
        Arrays.fill(minDist, Integer.MAX_VALUE);
        
        nodes = new LinkedList[nodeCount + 1];
        for (int i = 1; i <= nodeCount; i++) {
            nodes[i] = new LinkedList();
        }
        
        for (int i = 0; i < sideCount; i++) {
            Edge edge = new Edge(scanner.nextInt(), 
                scanner.nextInt(), scanner.nextInt());
            nodes[edge.from].add(edge);
        }
        
        int start = scanner.nextInt();
        int end = scanner.nextInt();
        int limit = scanner.nextInt();
        
        minDist[start] = 0;
        
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(start);
        
        // 松弛 limit + 1 次
        while (limit-- >= 0 && !queue.isEmpty()) {
            System.arraycopy(minDist, 0, minDistCopy, 0, minDistCopy.length);
            boolean visited[] = new boolean[nodeCount + 1];
            int queueSize = queue.size();
            // 松弛所有与已经松弛过的节点连接的边(节点)
            while (queueSize-- > 0) {
                int currentNode = queue.poll();
                for (Edge edge : nodes[currentNode]) {
                    if (minDistCopy[edge.from] != Integer.MAX_VALUE 
                        && minDistCopy[edge.from] + edge.value < minDist[edge.to]) {
                        minDist[edge.to] = minDistCopy[edge.from] + edge.value;
                        if (visited[edge.to]) {
                            continue;
                        }
                        queue.offer(edge.to);
                        visited[edge.to] = true;
                    }
                }
            }
        }
        
        if (minDist[end] == Integer.MAX_VALUE) {
            System.out.println("unreachable");
        } else {
            System.out.println(minDist[end]);
        }
    }
    
    private static class Edge {
        
        int from;
        int to;
        int value;
        
        public Edge(int from, int to, int value) {
            this.from = from;
            this.to = to;
            this.value = value;
        }
    }
}

自己实现过程中遇到哪些困难 

    无... 因为感觉自己是在背代码.

[KamaCoder] 97. 小明逛公园

[KamaCoder] 97. 小明逛公园 文章解释

题目描述

小明喜欢去公园散步,公园内布置了许多的景点,相互之间通过小路连接,小明希望在观看景点的同时,能够节省体力,走最短的路径。 

给定一个公园景点图,图中有 N 个景点(编号为 1 到 N),以及 M 条双向道路连接着这些景点。每条道路上行走的距离都是已知的。

小明有 Q 个观景计划,每个计划都有一个起点 start 和一个终点 end,表示他想从景点 start 前往景点 end。由于小明希望节省体力,他想知道每个观景计划中从起点到终点的最短路径长度。 请你帮助小明计算出每个观景计划的最短路径长度。

输入描述

第一行包含两个整数 N, M, 分别表示景点的数量和道路的数量。 

接下来的 M 行,每行包含三个整数 u, v, w,表示景点 u 和景点 v 之间有一条长度为 w 的双向道路。 

接下里的一行包含一个整数 Q,表示观景计划的数量。 

接下来的 Q 行,每行包含两个整数 start, end,表示一个观景计划的起点和终点。

输出描述

对于每个观景计划,输出一行表示从起点到终点的最短路径长度。如果两个景点之间不存在路径,则输出 -1。

输入示例
7 3
2 3 4
3 6 6
4 7 8
2
2 3
3 4
输出示例
4
-1
提示信息

从 2 到 3 的路径长度为 4,3 到 4 之间并没有道路。

1 <= N, M, Q <= 1000.

[KamaCoder] 97. 小明逛公园

自己看到题目的第一想法

    无

看完代码随想录之后的想法

    动态规划来了!!! 抄了那么多动态规划的代码, 我对动态规划依旧一无所知. 我真的适合写程序吗, 我有理解算法的能力吗? 除了坚持和重复, 没有别的想法了.

    本质上就是, 对于每一个路径, 都计算一下 从 i -> k -> j 的路径, 是否比直接从 i -> j 更近. 对于 grid[i][j][k] 有两种途径可以推导, 一个是 grid[i][j][k - 1], 一个是 grid[i][k][k - 1] + grid[k][j][k - 1]. grid[i][j][k - 1] 就是说, 我不从节点 k 走了, 直接依赖上一层推算出来的值. grid[i][k][k - 1] + grid[k][j][k - 1] 就是说, 我要去节点 k 转一下, 看看 i -> k -> j 这样的路径, 值是多少. grid[i][j][1] 就是 i -> j 的路径的权值. grid[i][j][2] 就是 i -> j 和 i -> 1 -> j 这两个路径选择更近的, 同理 grid[i][j][3] 就是 i -> j、i -> 1 ->j、i -> 2 -> j 三者中最近的那个. 动态规划的核心在这里了.

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;

public class Main {
    
    private static List<List<Edge>> nodes = new ArrayList<>();
    private static List<Edge> queryNodes = new ArrayList<>();
    private static int[][][] grid = null;

    public static void main(String[] args) {
        
        Scanner scanner = new Scanner(System.in);
        
        int nodeCount = 0;
        int sideCount = 0;
        
        nodeCount = scanner.nextInt();
        sideCount = scanner.nextInt();
        
        grid = new int[nodeCount + 1][nodeCount + 1][nodeCount + 1];
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length; j++) {
                for (int k = 0; k < grid[i][j].length; k++) {
                    grid[i][j][k] = Integer.MAX_VALUE/2;
                }
            }
        }
        
        int from;
        int to;
        int value;
        for (int i = 0; i < sideCount; i++) {
            from = scanner.nextInt();
            to = scanner.nextInt();
            value = scanner.nextInt();
            grid[from][to][0] = value;
            grid[to][from][0] = value;// 这里是双向图
        }
        
        int queryCount = scanner.nextInt();
        for (int i = 0; i < queryCount; i++) {
            queryNodes.add(new Edge(scanner.nextInt(), scanner.nextInt(), 0));
        }
        
        for (int k = 1; k <= nodeCount; k++) {
            for (int i = 1; i <= nodeCount; i++) {
                for (int j = 1; j <= nodeCount; j++) {
                    grid[i][j][k] = Math.min(grid[i][j][k - 1], 
                        grid[i][k][k - 1] + grid[k][j][k - 1]);
                }
            }
        }
        for (Edge edge : queryNodes) {
            if (grid[edge.from][edge.to][nodeCount] == Integer.MAX_VALUE/2) {
                System.out.println(-1);
            } else {
                System.out.println(grid[edge.from][edge.to][nodeCount]);
            }
        }
    }
    private static class Edge {
        
        int from;
        int to;
        int value;
        
        public Edge(int from, int to, int value) {
            this.from = from;
            this.to = to;
            this.value = value;
        }
    }
}
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;

public class Main {
    
    private static List<List<Edge>> nodes = new ArrayList<>();
    private static List<Edge> queryNodes = new ArrayList<>();
    private static int[][] grid = null;

    public static void main(String[] args) {
        
        Scanner scanner = new Scanner(System.in);
        
        int nodeCount = 0;
        int sideCount = 0;
        
        nodeCount = scanner.nextInt();
        sideCount = scanner.nextInt();
        
        grid = new int[nodeCount + 1][nodeCount + 1];
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length; j++) {
                grid[i][j] = Integer.MAX_VALUE/2;
            }
        }
        
        int from;
        int to;
        int value;
        for (int i = 0; i < sideCount; i++) {
            from = scanner.nextInt();
            to = scanner.nextInt();
            value = scanner.nextInt();
            grid[from][to] = value;
            grid[to][from] = value;// 这里是双向图
        }
        
        int queryCount = scanner.nextInt();
        for (int i = 0; i < queryCount; i++) {
            queryNodes.add(new Edge(scanner.nextInt(), scanner.nextInt(), 0));
        }
        
        for (int k = 1; k <= nodeCount; k++) {
            for (int i = 1; i <= nodeCount; i++) {
                for (int j = 1; j <= nodeCount; j++) {
                    grid[i][j] = Math.min(grid[i][j], grid[i][k] + grid[k][j]);
                }
            }
        }
        for (Edge edge : queryNodes) {
            if (grid[edge.from][edge.to] == Integer.MAX_VALUE/2) {
                System.out.println(-1);
            } else {
                System.out.println(grid[edge.from][edge.to]);
            }
        }
    }
    private static class Edge {
        
        int from;
        int to;
        int value;
        
        public Edge(int from, int to, int value) {
            this.from = from;
            this.to = to;
            this.value = value;
        }
    }
}

自己实现过程中遇到哪些困难 

    生成图的时候, 没有注意到是双向图, 只生成了单向图.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值