推箱子
力扣链接:1263. 推箱子
 
题目描述
「推箱子」是一款风靡全球的益智小游戏,玩家需要将箱子推到仓库中的目标位置。
游戏地图用大小为 m x n 的网格 grid 表示,其中每个元素可以是墙、地板或者是箱子。
现在你将作为玩家参与游戏,按规则将箱子 ‘B’ 移动到目标位置 ‘T’ :
玩家用字符 ‘S’ 表示,只要他在地板上,就可以在网格中向上、下、左、右四个方向移动。
 地板用字符 ‘.’ 表示,意味着可以自由行走。
 墙用字符 ‘#’ 表示,意味着障碍物,不能通行。
 箱子仅有一个,用字符 ‘B’ 表示。相应地,网格上有一个目标位置 ‘T’。
 玩家需要站在箱子旁边,然后沿着箱子的方向进行移动,此时箱子会被移动到相邻的地板单元格。记作一次「推动」。
 玩家无法越过箱子。
 返回将箱子推到目标位置的最小 推动 次数,如果无法做到,请返回 -1。
示例

 输入:grid = [[“#”,“#”,“#”,“#”,“#”,“#”],
 [“#”,“T”,“#”,“#”,“#”,“#”],
 [“#”,“.”,“.”,“B”,“.”,“#”],
 [“#”,“.”,“#”,“#”,“.”,“#”],
 [“#”,“.”,“.”,“.”,“S”,“#”],
 [“#”,“#”,“#”,“#”,“#”,“#”]]
 输出:3
 解释:我们只需要返回推箱子的次数。
Java代码
class Solution {
    //箱子视角进行BFS,人视角进行DFS,后者作为前者得以行进的前提
    public int minPushBox(char[][] grid) {
        int m = grid.length, n = grid[0].length;
        //遍历一次,找出箱子起点/终点,人的位置
        int startX = -1, startY = -1;
        int targetX = -1, targetY = -1;
        int personX = -1, personY = -1;
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(grid[i][j] == 'B') {
                    startX = i;
                    startY = j;
                }
                if(grid[i][j] == 'T') {
                    targetX = i;
                    targetY = j;
                    grid[i][j] = '.';
                }
                if(grid[i][j] == 'S') {
                    personX = i;
                    personY = j;
                    grid[i][j] = '.';
                }
            }
        }
        //初始化队列,加入元素以启动BFS
        boolean[][][] visited = new boolean[m][n][4];
        Queue<Box> queue = new LinkedList<>();
        for(int i = 0; i < 4; i++) {
            int[] direction = directions[i];
            if(personCanReach(grid, m, n, personX, personY, startX - direction[0], startY - direction[1], new boolean[m][n])) {
                queue.add(new Box(startX, startY, i));
                visited[startX][startY][i] = true;
            }
        }
        //以箱子的视角开始BFS
        int step = 0;
        while(!queue.isEmpty()) {
            int size = queue.size();
            while(size-- > 0) {
                Box box = queue.poll();
                grid[box.x][box.y] = 'B';
                personX = box.x - directions[box.from][0];
                personY = box.y - directions[box.from][1];
                if(box.x == targetX && box.y == targetY) {
                    return step;
                }
                for(int i=0; i < 4; i++) {
                    int[] direction = directions[i];
                    int nextX = box.x + direction[0];
                    int nextY = box.y + direction[1];
                    //人能否绕到箱子后面?
                    if(!personCanReach(grid, m, n, personX, personY, box.x - direction[0], box.y - direction[1], new boolean[m][n])) {
                        continue;
                    }
                    //箱子的下个位置是否合法?
                    if(!isValid(grid, m, n, nextX, nextY)) {
                        continue;
                    }
                    //箱子的下一个状态是否重复?
                    if(visited[nextX][nextY][i]) {
                        continue;
                    }
                    queue.add(new Box(nextX, nextY, i));
                    visited[nextX][nextY][i] = true;
                }
                grid[box.x][box.y] = '.';
            }
            step++;
        }
        return -1;
    }
    //其含义是上/下/左/右变换的动作
    private final static int[][] directions = {{-1,0}, {1,0}, {0,-1}, {0,1}};
    //静态内部类是个顶级类,可以当成外部类来看
    private static class Box{
        int x, y;
        int from;
        public Box(int x, int y, int from) {
            this.x = x;
            this.y = y;
            this.from = from;
        }
    }
    //人是否可以从某一位置(startX, startY)到达另一位置(targetX, targetY)
    private boolean personCanReach(char[][] grid, int m, int n, int startX, int startY, int targetX, int targetY, boolean[][] visited) {
        if(startX == targetX && startY == targetY) {
            return true;
        }
        visited[startX][startY] = true;
        for(int[] direction : directions) {
            int nextX = startX + direction[0];
            int nextY = startY + direction[1];
            if(isValid(grid, m, n, nextX, nextY) && !visited[nextX][nextY]) {
                if(personCanReach(grid, m, n, nextX, nextY, targetX, targetY, visited)) {
                    return true;
                }
            }
        }
        return false;
    }
    //某位置是否可以踏足
    private boolean isValid(char[][] grid, int m, int n, int x, int y) {
        return x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '.';
    }
}
来源:力扣(LeetCode)
 链接:https://leetcode.cn/problems/minimum-moves-to-move-a-box-to-their-target-location
 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。



















