力扣原题,java代码
解题思路:
本题考查的知识点是广度优先和动态规划,以箱子当作主体,人可以从上下左右四个方向推箱子,故而对于箱子的每个位置,我们需要考虑人从不同的方向推箱子产生的代价(即箱子从初始位置到当前位置的最小移动次数)。
故而int[][] dp = new int[n*m][4];
dp[bnew][k]:表示人从方向k将箱子移动到新位置bnew的最小移动次数
备注:人从初始位置推箱子,箱子移动后的最小移动次数一定是1.
初始化:将箱子的初始位置,人的初始位置,初始方向(因为初始情况不存在箱子的移动,所以没有初始方向,设置为-1,与后面的情况要分开讨论)添加入队列中。(备注:队列先进先出适合广度优先,堆栈先进后出符合深度优先)
1.人从箱子的-k侧将箱子推移到箱子的k侧(备注:若k侧表示左侧,则-k侧就表示右侧)
①判断箱子移动后的位置和人推箱子的位置是否越界;
②若当前是初始位置,则将箱子移动后dp[bnew][k]=1;
若不是初始位置,则判断 人从方向k将箱子移动到位置bnew 这种情况是否已经探索过,若探索过,则不再探索,若没有探索,则探索该种情况;
③判断人是否能从当前位置到达箱子的-k侧(判断箱子的-k侧与人的位置是否是联通的,注意:箱子是不能越过的)
④若箱子移动后的位置与目标位置重合,则返回 dp[bnew][k];
反之,则将箱子移动后的位置和人当前的位置以及移动方向存入队列中,继续当前循环。
import java.util.*;
class Solution {
int[] d = new int[]{0,1,0,-1,0};
public int minPushBox(char[][] grid) {
int n = grid.length, m = grid[0].length;
int s=-1, b=-1; // 广度优先
int[][] dp = new int[n*m][4]; // 箱子和人的位置
for(int i=0; i<n; i++) {
for (int j = 0; j<m; j++) {
Arrays.fill(dp[i*m+j],400);
if(grid[i][j] == 'S') {
s = i*m+j;
} else if(grid[i][j] == 'B') {
b = i*m+j;
}
}
}
//Arrays.fill(dp[b],0);
Queue<int[]> q = new LinkedList<>(); // 箱子的位置
q.add(new int[]{b,s,-1});
while (!q.isEmpty()) {
int[] bs = q.poll();
int bi = bs[0]/m, bj=bs[0]%m;
for (int k = 0; k < 4; k++) {
// 人从箱子的-k侧将箱子推移到箱子的k侧
int i = bi+d[k], j=bj+d[k+1]; // 箱子的新位置
int u = bi-d[k], v=bj-d[k+1]; // 人推箱子的位置
int kp = bs[2]; // 箱子和人的位置,人从哪个方向推箱子
int bnew = i*m+j;
if(isOk(grid,i,j,n,m) && isOk(grid,u,v,n,m) &&
(kp == -1 || dp[bnew][k] > dp[bs[0]][kp]+1)) {
// 人是否能从当前位置达到箱子的-k侧
boolean[] vis = new boolean[n*m]; vis[bs[0]] = true; // 箱子的位置不能被访问
if(isFeaseable(grid,bs[1],u*m+v,vis,n,m)) {
// 当前箱子可以被人由-k推到k
dp[bnew][k] = kp==-1?1:dp[bs[0]][kp]+1; // 箱子从原本的位置移动1步
if(grid[i][j] == 'T') {
return dp[bnew][k];
}
q.add(new int[]{bnew,bs[0],k});
}
}
}
}
return -1;
}
// 人当前的位置与箱子k侧的位置联通
public boolean isFeaseable(char[][] grid, int s, int b, boolean[] vis, int n, int m) {
if(s == b) {
return true;
}
Queue<Integer> q = new LinkedList<>();
q.add(b);
while (!q.isEmpty()) {
int bij = q.poll();
int bi=bij/m, bj=bij%m;
for (int k = 0; k < 4; k++) {
int i=bi+d[k], j=bj+d[k+1];
int b1 = i*m+j;
if(isOk(grid,i,j,n,m) && !vis[b1]) {
if(s == b1) {
return true;
}
vis[b1] = true;
q.add(b1);
}
}
}
return false;
}
public boolean isOk(char[][] grid, int i, int j, int n, int m) {
if(i>=0 && i<n && j>=0 && j<m && grid[i][j] != '#') {
return true;
}
return false;
}
}