OD机试 - 智能驾驶 - 广度优先搜索

题目

有一辆汽车需要从 m * n 的地图的左上角(起点)开往地图的右下角(终点 ),去往每一个地区都需要消耗一定的油量,加油站可进行加油

请你计算汽车确保从起点到达终点时所需的最少初始油量

说明:

(1)智能汽车可以上下左右四个方向移动;

(2)地图上的数字取值是 0 或 −1 或者正整数;

−1:表示加油站,可以加满油,汽车的油箱容量最大为 100;

0 :表示这个地区是障碍物,汽车不能通过;

正整数:表示汽车走过这个地区的耗油量

(3)如果汽车无论如何都无法到达终点,则返回 −1

输入描述

第一行为两个数字,M , N,表示地图的大小为 M * N ( 0 < M,N ≤ 200 )

后面一个M * N 的矩阵,其中的值是 0 或 −1 或正整数,加油站的总数不超过 200个

输出描述

如果汽车无论如何都无法到达终点,则返回-1

如果汽车可以到达终点,则返回最少的初始油量

样例

输入

2 2
10 20
30 40

输出

70

说明

行走的路线为:右 -> 下

理解

1.因为存在加油站和障碍物,所以初始油量取决于路径,因此探索过程采用二分法确定初始油量逐步探索,减少探索次数
2.题目是需要最少油量,所以探索过程需要探索最优秀路线,以样例为例
(0,0)需要10 ,(0,1)需要20 ,(1,0)需要30 ,(1,1)需要40,(0,1)和(1,0)代表不同路径,(1,1)是共同经过的点,则在经过1,1的时候应该做出对比,发现 (0,0),(0,1),(1,1)的路线比(0,0),(1,0),(1,1)省油,则(0,0),(1,0),(1,1)的路径应该放弃,即:每次探索都探索各自的探索方向,将下一个可探索的方向收集起来,收集的过程要判断是否已被别的路径探索过,如果有,则比较路径优劣

代码

import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;

public class Test025 {
        /*
        * 有一辆汽车需要从 m * n 的地图的左上角(起点)开往地图的右下角(终点 ),去往每一个地区都需要消耗一定的油量,加油站可进行加油
        * 请你计算汽车确保从起点到达终点时所需的最少初始油量
        * 说明:
        * (1)智能汽车可以上下左右四个方向移动;
        * (2)地图上的数字取值是 0 或 −1 或者正整数;
        * −1:表示加油站,可以加满油,汽车的油箱容量最大为 100;
        * 0 :表示这个地区是障碍物,汽车不能通过;
        * 正整数:表示汽车走过这个地区的耗油量
        * (3)如果汽车无论如何都无法到达终点,则返回 −1
        *
        * */
        // 探索位移的方向
        static int[] DX = {0, 1, 0, -1};
        static int[] DY = {1, 0, -1, 0};
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            int[] rc = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
            int row = rc[0];
            int col = rc[1];
            // 矩阵信息图
            int[][] mapArr = new int[row][col];
            for (int i = 0; i < row; i++) {
                for (int j = 0; j < col; j++) {
                    mapArr[i][j] = scanner.nextInt();
                }
            }
            if (mapArr[0][0] == 0 || mapArr[row - 1][col - 1] == 0) {
                // 起点或终点是障碍物,不可达到
                System.out.println(-1);
                return;
            }
            // 最低油量
            int min = 0;
            // 最多油量
            int max = 100;
            // 最少油量
            int least = -1;
            // 定义携带油量 carry
            int carry = (min + max) / 2;
            // 二分查找 当最少等于最多时,则探索完成
            while (min <= max) {
                // 判断当前油量是否能完成探索
                if (checkEnough(mapArr, carry, row, col)) {
                    least = carry;
                    max = carry - 1;
                } else {
                    min = carry + 1;
                }
                carry = (min + max) / 2;
            }
            System.out.println(least);
        }

        private static boolean checkEnough(int[][] mapArr, int carry, int n, int m) {
            // 存储访问探索过的节点
            boolean[][] visited = new boolean[n][m];
            // 存储剩余油量
            int[][] remainArr = new int[n][m];
            // 读取矩阵的每一个元素
            for (int i = 0; i < n; i++) {
                Arrays.fill(remainArr[i], -1);
            }
            // 移动到第一格剩余油量
            remainArr[0][0] = carry - mapArr[0][0];
            // 如果剩余油量负数,则说明到达不了,返回
            if (remainArr[0][0] < 0) {
                return false;
            }
            if (mapArr[0][0] == -1) {
                remainArr[0][0] = 100;
            }
            // 使用优先队列存储状态 顺序剩余油量少者优先
            PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> b[0] - a[0]);
            q.offer(new int[] {remainArr[0][0], 0, 0});  // 将初始状态加入队列
            while (!q.isEmpty()) {
                int[] cur = q.poll();
                int curX = cur[1];
                int curY = cur[2];
                if (visited[curX][curY]) {
                    continue;
                }
                int curRemain = cur[0];
                visited[curX][curY] = true;
                for (int i = 0; i < 4; i++) {
                    int nextX = curX + DX[i];
                    int nextY = curY + DY[i];
                    // 越界
                    // 下一个地点是障碍物 mapArr[nextX][nextY] == 0
                    // 下一个地点探索过 visited[nextX][nextY]
                    // 下一个地点 剩余油量不足到达
                    if (nextX < 0 || nextX >= n || nextY < 0 || nextY >= m
                        || mapArr[nextX][nextY] == 0
                        || visited[nextX][nextY]
                        || mapArr[nextX][nextY] > curRemain) {
                        continue;
                    }
                    // 到达终点
                    if (nextX == n - 1 && nextY == m - 1) {
                        return true;
                    }
                    // 探索此点剩余油量
                    int remain = mapArr[nextX][nextY] == -1 ? 100 : curRemain - mapArr[nextX][nextY];
                    // 需注意,remainArr[nextX][nextY] 可能已经被前面的路径探索过,所以对比路径优劣
                    // 如果此路径剩余油量比前面路径多,则继续探索此路径
                    if (remain > remainArr[nextX][nextY]) {
                        remainArr[nextX][nextY] = remain;
                        q.offer(new int[] {remainArr[nextX][nextY], nextX, nextY});
                    }
                }
            }
            return visited[n - 1][m - 1];
        }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值