必刷算法100题之为高尔夫比赛砍树

题目链接

675. 为高尔夫比赛砍树 - 力扣(LeetCode)

题目解析

注意: 由低到高来砍树, 砍了1然后去找2(最短路),砍了2去找3(最短路)...按照顺序把所有的树都砍完

算法原理

砍树的顺序

我们找到每俩个点的最短距离, 就相当于若干个迷宫问题 

1. 砍树的顺序

把下标存在数组里面, 然后按照下标里面的值从小到大进行排序, 那么二维数组里面就存着待砍树的顺序了, 然后遍历二维数组, 就晓得从哪里砍到哪, 然后把所有的最小步数位置都累加起来, 如果走不到就返回-1.

代码编写

class Solution {

    public int n, m;

    public int cutOffTree(List<List<Integer>> forest) {
        // 记录每一个元素的下标
        n = forest.size();
        m = forest.get(0).size();
        List<int[]> treeIndex = new ArrayList<>();
        // 把所有的元素放入
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (forest.get(i).get(j) > 1) {
                    treeIndex.add(new int[] { i, j });
                }
            }
        }
        // 把这些下标按照值从大到小进行排序
        Collections.sort(treeIndex, (a, b) -> {
            // a, b代表的是treeIndex里面的记录元素下标的数组, 是treeIndex的元素
            return forest.get(a[0]).get(a[1]) - forest.get(b[0]).get(b[1]);
        });
        // 按照顺序来进行砍树 
        int rs = 0;// 用来记录最小步数的总步数
        int bx = 0, by = 0;// 用来记录起始位置
        // 遍历treeIndex
        for (int[] x : treeIndex) {
            // 记录终止位置
            int ex = x[0], ey = x[1];
            // 把起始和终止位置传入bfs进行最小值的查询
            int step = bfs(forest, bx, by, ex, ey);
            if (step == -1) {
                // 遇到墙了
                return -1;
            }
            rs += step;
            // 更新起始位置
            bx = ex;
            by = ey;
        }
        return rs;
    }

    // 坐标数组
    int[] dx = { 0, 0, -1, 1 };
    int[] dy = { -1, 1, 0, 0 };

    public int bfs(List<List<Integer>> forest, int bx, int by, int ex, int ey) {
        // 起始等于终止, 直接返回即可
        if (bx == ex && by == ey) {
            return 0;
        }
        // 使用队列进行bfs
        // 记录下标
        Queue<int[]> q = new LinkedList<>();
        // 记录每个下标是否第一次经过
        boolean[][] judge = new boolean[n][m];
        // 把起始下标先入队
        q.add(new int[] { bx, by });
        // 每次入队就修改状态
        judge[bx][by] = true;
        // 记录从起始到终止的步数
        int step = 0;
        while (!q.isEmpty()) {
            // 记录每一层的大小
            int sz = q.size();
            step++;
            for (int i = 0; i < sz; i++) {
                // 出队
                int[] tmp = q.poll();
                int a = tmp[0];
                int b = tmp[1];
                // 进行上下左右
                for (int j = 0; j < 4; j++) {
                    // 上下左右的下标
                    int x = a + dx[j];
                    int y = b + dy[j];
                    // 进行判断
                    if (x >= 0 && x < n && y >= 0 && y < m && forest.get(x).get(y) != 0 && !judge[x][y]) {
                        if (x == ex && y == ey) {
                            return step;
                        }
                        // 进行入队操作
                        q.add(new int[] { x, y });
                        // 修改状态
                        judge[x][y] = true;
                    }
                }
            }
        }
        // 说明有墙
        return -1;

    }
}

进行排序的构造, 对于对象而言要指定对什么进行排序

我们建立下标和值的关联

使用一个下标数组, 记录每个元素的下标

第二次刷

class Solution {
    // 分解成多个迷宫问题
    int n, m;

    public int cutOffTree(List<List<Integer>> f) {
        // 记录长宽
        n = f.size();
        m = f.get(0).size();
        // 记录每一棵树的下标, 并且按照值从小到大进行排序
        List<int[]> indexs = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                // >1 代表是树
                if (f.get(i).get(j) > 1) {
                    indexs.add(new int[] { i, j });
                }
            }
        }
        // 把indexs里面的下标按照数组的值来从小到大进行排序
        Collections.sort(indexs, (a, b) -> {
            return f.get(a[0]).get(a[1]) - f.get(b[0]).get(b[1]);
        });
        // 进行多个迷宫问题的bfs
        int bex = 0; // 记录起始位置
        int bey = 0;
        int rs = 0;// 记录总步数
        for (int[] x : indexs) {
            int endx = x[0];
            int endy = x[1];
            int step = bfs(f, bex, bey, endx, endy);
            // 对步数进行有效判断
            if (step == -1) {
                return -1;
            }
            rs += step;
            // 更新起始下标
            bex = endx;
            bey = endy;
        }
        // 返回总步数
        return rs;
    }

    // 坐标数组
    int[] dx = { 0, 0, -1, 1 };
    int[] dy = { -1, 1, 0, 0 };

    public int bfs(List<List<Integer>> f, int bex, int bey, int endx, int endy) {
        // 如果一开始be==ed就不需要bfs了
        if (bex == endx && bey == endy) {
            return 0;
        }
        // 使用队列来进行bfs
        Queue<int[]> q = new LinkedList<>();
        // 记录是否第一次遍历到这个位置
        boolean[][] juege = new boolean[n][m];
        // 把起始位置放入
        q.add(new int[] { bex, bey });
        // 修改状态的值
        juege[bex][bey] = true;
        int step = 0;// 记录用的最小的步数
        // 进入bfs
        while (!q.isEmpty()) {
            step++;
            // 记录层数
            int sz = q.size();
            // 每一层每一个元素同时向上下左右四个方向拓展
            for (int i = 0; i < sz; i++) {
                // 记录临时元素
                int[] tmp = q.poll();
                // 保存下标
                int a = tmp[0];
                int b = tmp[1];
                // 上下左右四个方向进行拓展
                for (int k = 0; k < 4; k++) {
                    // 计算下标
                    int x = a + dx[k];
                    int y = b + dy[k];
                    // 进行判断
                    if (x >= 0 && x < n && y >= 0 && y < m && !juege[x][y] && f.get(x).get(y) != 0) {
                        // 是不是终止位置
                        if (x == endx && y == endy) {
                            return step;
                        }
                        // 入队
                        q.add(new int[] { x, y });
                        // 修改状态
                        juege[x][y] = true;
                    }
                }
            }
        }
        // 说明有墙
        return -1;
    }
}

一开始进行判断不要漏了起始就是终止

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值