链接:https://www.nowcoder.com/questionTerminal/1f8d61e3090644d8996fdec01694d3cf
来源:牛客网
有一个推箱子的游戏, 一开始的情况如下图:
上图中, '.' 表示可到达的位置, '#' 表示不可到达的位置,其中 S 表示你起始的位置, 0表示初始箱子的位置, E表示预期箱子的位置,你可以走到箱子的上下左右任意一侧, 将箱子向另一侧推动。如下图将箱子向右推动一格;
..S0.. -> ...S0.
注意不能将箱子推动到'#'上, 也不能将箱子推出边界;
现在, 给你游戏的初始样子, 你需要输出最少几步能够完成游戏, 如果不能完成, 则输出-1。
输入描述:
第一行为2个数字,n, m, 表示游戏盘面大小有n 行m 列(5< n, m < 50);
后面为n行字符串,每行字符串有m字符, 表示游戏盘面;
输出描述:
一个数字,表示最少几步能完成游戏,如果不能,输出-1;
示例1
输入
3 6
.S#..E
.#.0..
......
输出
11
难点:
1、最短步数,可以使用BFS求得
常规BFS,可用Boolean数组记录是否已到达,避免重复添加路径。仅使用Boolean数组即可判断当前路径已走过的前提是,BFS每一层默认步数+1,假设当前位置为P,则从初始位置S到P的最短路径即为最开始搜索到P的路径,故若P已被访问过,则最短路径已被找到,以后再搜索到P,最短路径也不可能短于最初的最短路径了。
但此题有特殊之处,箱子每推动一次,人走的路程由两部分组成:Step1、由原来的位置走到箱子的某一侧的最短步数,Step2、向前推动一步。其中Step1并非固定为1,有可能是0或2,若途中有障碍,甚至会更长,因此仅用Boolean数组已不能确定访问过的P即是最短路径了,需要记录访问到P的最短步数,并比较,若当前的最短步数小于记录的最短步数,需要更新,否则可抛弃。
代码中的visit数组含义为:以往箱子推到位置P时花费的最少步数。
Step1本身求取可使用常规BFS,但箱子的路径添加需要记录每个位置的最短步数
2、此外,注意人不能从箱子所在的位置经过,因此要时时更新箱子的位置
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import static java.lang.System.out;
public class Main_2 {
public static boolean check(char[][] board,int r,int c,int target_i,int target_j){
if(target_i<0||target_j<0||target_i>=r||target_j>=c) return false;
char ch=board[target_i][target_j];
if(ch=='#'||ch=='0') return false;//
return true;
}
public static int SD(char[][] board,int r0,int c0,int rt,int ct){
int n=board.length,m=board[0].length;
if(!check(board,n,m,rt,ct)) return -1;
if(r0==rt&&c0==ct) return 0;
boolean[][] visit=new boolean[n][m];
Queue<int[]> q=new LinkedList<int[]>();
int[] cur={r0,c0,0};
visit[r0][c0]=true;
q.offer(cur);
while(!q.isEmpty()){
cur=q.poll();
if(check(board,n,m,cur[0]+1,cur[1])&&visit[cur[0]+1][cur[1]]==false){
if(cur[0]+1==rt&&cur[1]==ct) return cur[2]+1;
visit[cur[0]+1][cur[1]]=true;
int[] tmp={cur[0]+1,cur[1],cur[2]+1};
q.offer(tmp);
}
if(check(board,n,m,cur[0]-1,cur[1])&&visit[cur[0]-1][cur[1]]==false){
if(cur[0]-1==rt&&cur[1]==ct) return cur[2]+1;
visit[cur[0]-1][cur[1]]=true;
int[] tmp={cur[0]-1,cur[1],cur[2]+1};
q.offer(tmp);
}
if(check(board,n,m,cur[0],cur[1]+1)&&visit[cur[0]][cur[1]+1]==false){
if(cur[0]==rt&&cur[1]+1==ct) return cur[2]+1;
visit[cur[0]][cur[1]+1]=true;
int[] tmp={cur[0],cur[1]+1,cur[2]+1};
q.offer(tmp);
}
if(check(board,n,m,cur[0],cur[1]-1)&&visit[cur[0]][cur[1]-1]==false){
if(cur[0]==rt&&cur[1]-1==ct) return cur[2]+1;
visit[cur[0]][cur[1]-1]=true;
int[] tmp={cur[0],cur[1]-1,cur[2]+1};
q.offer(tmp);
}
}
return -1;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n=scan.nextInt(),m=scan.nextInt();
scan.nextLine();
char[][] board=new char[n][m];
int [][] visit=new int[n][m];
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
visit[i][j]=Integer.MAX_VALUE;
for(int i=0;i<n;i++){
board[i]=scan.nextLine().toCharArray();
}
int h_i=-1,h_j=-1;
int b_i=-1,b_j=-1;
int e_i=-1,e_j=-1;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
if(board[i][j]=='S'){
h_i=i;
h_j=j;
}
if(board[i][j]=='0'){
b_i=i;
b_j=j;
}
if(board[i][j]=='E'){
e_i=i;
e_j=j;
}
}
int max=Integer.MAX_VALUE;
Queue<int[]> q=new LinkedList<int[]>();
int[] s={b_i,b_j,h_i,h_j,0};
visit[b_i][b_j]=0;
board[b_i][b_j]='.';
board[h_i][h_j]='.';
q.offer(s);
// out.println("终点:("+e_i+","+e_j+")");
while(!q.isEmpty()){
int[] cur=q.poll();
board[cur[0]][cur[1]]='0';
if(cur[4]>=max){
board[cur[0]][cur[1]]='.';
continue;
}
// out.println("箱子所在位置: ("+cur[0]+","+cur[1]+")");
if(check(board,n,m,cur[0]+1,cur[1])){//&&visit[cur[0]+1][cur[1]]==false
int add=SD(board,cur[2],cur[3],cur[0]-1,cur[1])+1;
if(add>0&&cur[4]+add<max) {
// out.println("人原来的位置: ("+cur[2]+","+cur[3]+") , 现在的位置(向下推): ("+(cur[0])+","+cur[1]+"), 要走 "+(add)
// +"步"+" , 已走 "+cur[4]+"步 , 箱子现在位置: ("+(cur[0]+1)+","+cur[1]+")");
if (board[cur[0] + 1][cur[1]] == 'E') {
max = cur[4] + add;
// out.println("更新max "+max);
}
else{
int[] tmp = {cur[0] + 1, cur[1], cur[0], cur[1], cur[4] + add};
if(visit[cur[0]+1][cur[1]]>cur[4] + add) {
visit[cur[0] + 1][cur[1]] = cur[4] + add;
q.offer(tmp);
}
}
}
}
if(check(board,n,m,cur[0]-1,cur[1])){//&&visit[cur[0]-1][cur[1]]==false
int add=SD(board,cur[2],cur[3],cur[0]+1,cur[1])+1;
if(add>0&&cur[4]+add<max) {
// out.println("人原来的位置: ("+cur[2]+","+cur[3]+") , 现在的位置(向上推): ("+(cur[0])+","+cur[1]+"), 要走 "+
// (add)+"步"+" , 已走 "+cur[4]+"步 , 箱子现在位置: ("+(cur[0]-1)+","+cur[1]+")");
if (board[cur[0] - 1][cur[1]] == 'E') {
max = cur[4] + add;
// out.println("更新max "+max);
}
else {
int[] tmp = {cur[0] - 1, cur[1], cur[0], cur[1], cur[4] + add};
if(visit[cur[0]-1][cur[1]]>cur[4] + add) {
visit[cur[0] - 1][cur[1]] = cur[4] + add;
q.offer(tmp);
}
}
}
}
if(check(board,n,m,cur[0],cur[1]+1)){//&&visit[cur[0]][cur[1]+1]==false
int add=SD(board,cur[2],cur[3],cur[0],cur[1]-1)+1;
if(add>0&&cur[4]+add<max) {
// out.println("人原来的位置: ("+cur[2]+","+cur[3]+") , 现在的位置(向右): ("+(cur[0])+","+(cur[1])+"), 要走 "+
// (add)+"步"+" , 已走 "+cur[4]+"步 , 箱子现在位置: ("+cur[0]+","+(cur[1]+1)+")");
if (board[cur[0]][cur[1] + 1] == 'E') {
max = cur[4] + add;
// out.println("更新max "+max);
}
else {
int[] tmp = {cur[0], cur[1] + 1, cur[0], cur[1], cur[4] + add};
if(visit[cur[0]][cur[1]+1]>cur[4] + add) {
visit[cur[0]][cur[1]+1] = cur[4] + add;
q.offer(tmp);
}
}
}
}
if(check(board,n,m,cur[0],cur[1]-1)){//&&visit[cur[0]][cur[1]-1]==false
int add=SD(board,cur[2],cur[3],cur[0],cur[1]+1)+1;
if(add>0&&cur[4]+add<max) {
// out.println("人原来的位置: ("+cur[2]+","+cur[3]+") , 现在的位置(向左推): ("+(cur[0])+","+(cur[1])+"), 要走 "+
// (add)+"步"+" , 已走 "+cur[4]+"步 , 箱子现在位置: ("+cur[0]+","+(cur[1]-1)+")");
if (board[cur[0]][cur[1] - 1] == 'E') {
max = cur[4] + add;
// out.println("更新max "+max);
}
else {
int[] tmp = {cur[0], cur[1] - 1, cur[0], cur[1], cur[4] + add};
if(visit[cur[0]][cur[1]-1]>cur[4] + add) {
visit[cur[0]][cur[1]-1] = cur[4] + add;
q.offer(tmp);
}
}
}
}
board[cur[0]][cur[1]]='.';
}
out.println(max==Integer.MAX_VALUE?-1:max);
}
}