推箱子
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 9635 Accepted Submission(s): 2835
Problem Description
推箱子是一个很经典的游戏.今天我们来玩一个简单版本.在一个M*N的房间里有一个箱子和一个搬运工,搬运工的工作就是把箱子推到指定的位置,注意,搬运工只能推箱子而不能拉箱子,因此如果箱子被推到一个角上(如图2)那么箱子就不能再被移动了,如果箱子被推到一面墙上,那么箱子只能沿着墙移动.
现在给定房间的结构,箱子的位置,搬运工的位置和箱子要被推去的位置,请你计算出搬运工至少要推动箱子多少格.
现在给定房间的结构,箱子的位置,搬运工的位置和箱子要被推去的位置,请你计算出搬运工至少要推动箱子多少格.
![](https://i-blog.csdnimg.cn/blog_migrate/9cd43be9c80ad0a119b921c7d0d1855d.jpeg)
Input
输入数据的第一行是一个整数T(1<=T<=20),代表测试数据的数量.然后是T组测试数据,每组测试数据的第一行是两个正整数M,N(2<=M,N<=7),代表房间的大小,然后是一个M行N列的矩阵,代表房间的布局,其中0代表空的地板,1代表墙,2代表箱子的起始位置,3代表箱子要被推去的位置,4代表搬运工的起始位置.
Output
对于每组测试数据,输出搬运工最少需要推动箱子多少格才能帮箱子推到指定位置,如果不能推到指定位置则输出-1.
Sample Input
15 50 3 0 0 01 0 1 4 00 0 1 0 01 0 2 0 00 0 0 0 0
Sample Output
4
思路:
如果这个箱子不用人来移动,自己就可以移动,那么这道题就是一个最简单的bfs题。但是这道题的问题在于箱子需要人来推,而人不一定能够到达推箱子的位置。那么我们需要另外写一个bfs,来判断当前情况中,人能不能到达这个位置。这个bfs形式上是很简单的,没有多余条件,只需要传入路径,起始位置,结束位置,然后每个位置不能到两次,简单剪个枝,返回true or false 就行了。但是接下来又有一个问题:路径是会改变的。每次移动箱子,人的位置会变,路径也会变。那么相当于人和路径都是跟着箱子走的,是箱子的一个属性。同样的要写进Node类里,跟着Node对象走。最后就是箱子是可以到同一个位置两次的。但是如果你就直接四个方向什么都不管地搜索,这样是要超时的。解决办法就是vis的三维数组记录,同样的方向不能到达两次,这样来剪枝。
代码:
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
static int[][] road;
static boolean[][][] vis;
static boolean[][] visman;
static int[][] dir={{0,1},{1,0},{0,-1},{-1,0}}; //第一组是向右,二是向下,三是向左,四是向上
static Node start;
static Node end;
static man man123;
static int M,N;
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner cin=new Scanner(System.in);
int T=cin.nextInt();
while(T-->0)
{
M=cin.nextInt();
N=cin.nextInt();
road=new int[M][N];
vis=new boolean[M][N][4] ;
for(int i=0;i<M;i++)
{
for(int j=0;j<N;j++)
{
road[i][j]=cin.nextInt();
if(road[i][j]==2)start=new Node(i,j,0,0);
if(road[i][j]==3)end=new Node(i,j,0,0);
if(road[i][j]==4)man123=new man(i,j);
}
}
start.mann=man123;
for(int i=0;i<M;i++)
{
for(int j=0;j<N;j++)
{
start.roadd[i][j]=road[i][j];
}
}
System.out.println(bfs(road));
}
}
static int bfs(int[][] road)
{
Queue<Node> queue=new LinkedList<Node>();
queue.add(start);
while(!queue.isEmpty())
{
Node node=queue.poll();
if(node.x==end.x&&node.y==end.y)return node.number;
vis[node.x][node.y][node.direct]=true;
for(int i=0;i<4;i++)
{
visman=new boolean[M][N];
int new_row=node.x+dir[i][0];
int new_col=node.y+dir[i][1];
if(new_row<0||new_col<0||new_row>=M||new_col>=N||vis[new_row][new_col][i]==true)continue;
if(i==0||i==2)
{
int yy=node.y-dir[i][1];
if(yy<0||yy>=N)continue; //箱子两边都要有位置,为防止数组越界,先判断有没有位置,再判断是不是墙
else
if(road[node.x][node.y+dir[i][1]]==1||road[node.x][node.y-dir[i][1]]==1)continue;
}
if(i==1||i==3)
{
int xx=node.x-dir[i][0];
if(xx<0||xx>=M)continue;
else
if(road[node.x+dir[i][0]][node.y]==1||road[node.x-dir[i][0]][node.y]==1)continue;
}
if(i==0||i==2)
{
if(bfsman(node.roadd,node.mann,new man(node.x,node.y-dir[i][1]))){
Node nn=new Node(new_row,new_col,node.number+1,i);
for(int m=0;m<M;m++) //这个for的作用就是路径的改变是在上一次路径的基础之上,而不是最初的那个路径,所以先赋值
{
for(int n=0;n<N;n++)
{
nn.roadd[m][n]=node.roadd[m][n];
}
}
nn.mann=new man(node.x,node.y);
nn.roadd[node.x][node.y+dir[i][1]]=2;
nn.roadd[node.x][node.y]=0;
queue.offer(nn);
}
}
else if(i==1||i==3)
{
if(bfsman(node.roadd,node.mann,new man(node.x-dir[i][0],node.y))){
Node nn=new Node(new_row,new_col,node.number+1,i);
for(int m=0;m<M;m++)
{
for(int n=0;n<N;n++)
{
nn.roadd[m][n]=node.roadd[m][n];
}
}
nn.mann=new man(node.x,node.y);
nn.roadd[node.x+dir[i][0]][node.y]=2;
nn.roadd[node.x][node.y]=0;
queue.offer(nn);
}
}
}
}
return -1;
}
static boolean bfsman(int[][] road,man start,man end)
{
Queue<man> queue=new LinkedList<man>();
queue.add(start);
while(!queue.isEmpty())
{
man node=queue.poll();
visman[node.x][node.y]=true;
if(node.x==end.x&&node.y==end.y)return true;
for(int i=0;i<4;i++)
{
int row=node.x+dir[i][0];
int col=node.y+dir[i][1];
if(row<0||col<0||row>=M||col>=N||road[row][col]==2||road[row][col]==1||visman[row][col]==true)continue;
man nn=new man(row,col);
queue.offer(nn);
}
}
return false;
}
static class Node
{
int x;
int y;
int number;
int[][] roadd=new int[M][N];
man mann=new man();
int direct=-1;
Node(int xx,int yy,int num,int dd)
{
x=xx;
y=yy;
number=num;
for(int i=0;i<M;i++)
{
for(int j=0;j<N;j++)
{
roadd[i][j]=road[i][j];
}
}
direct=dd;
}
public Node() {
// TODO 自动生成的构造函数存根
}
}
static class man
{
int x;
int y;
man(int xx,int yy)
{
x=xx;
y=yy;
}
public man() {
// TODO 自动生成的构造函数存根
}
}
}