推箱子
力扣链接: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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。