推箱子
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6549 Accepted Submission(s): 1866
Problem Description
推箱子是一个很经典的游戏.今天我们来玩一个简单版本.在一个M*N的房间里有一个箱子和一个搬运工,搬运工的工作就是把箱子推到指定的位置,注意,搬运工只能推箱子而不能拉箱子,因此如果箱子被推到一个角上(如图2)那么箱子就不能再被移动了,如果箱子被推到一面墙上,那么箱子只能沿着墙移动.
现在给定房间的结构,箱子的位置,搬运工的位置和箱子要被推去的位置,请你计算出搬运工至少要推动箱子多少格.

现在给定房间的结构,箱子的位置,搬运工的位置和箱子要被推去的位置,请你计算出搬运工至少要推动箱子多少格.

Input
输入数据的第一行是一个整数T(1<=T<=20),代表测试数据的数量.然后是T组测试数据,每组测试数据的第一行是两个正整数M,N(2<=M,N<=7),代表房间的大小,然后是一个M行N列的矩阵,代表房间的布局,其中0代表空的地板,1代表墙,2代表箱子的起始位置,3代表箱子要被推去的位置,4代表搬运工的起始位置.
Output
对于每组测试数据,输出搬运工最少需要推动箱子多少格才能帮箱子推到指定位置,如果不能推到指定位置则输出-1.
Sample Input
1 5 5 0 3 0 0 0 1 0 1 4 0 0 0 1 0 0 1 0 2 0 0 0 0 0 0 0
Sample Output
4
0ms。
思路:7*7,直接暴力枚举所有状态即可。
首先若把箱子从(x, y)推到(x+1, y),则人必须在(x-1, y)位置,同理其它三种情况也是这样。只需记录该状态下箱子的位置(x, y)和人的位置(mx, my),判断(mx, my)能否到达(x-1, y)位置即可。这样写好后,提交就WA了。
仔细推敲后,发现在人三面被堵且只有一面是箱子的情况下(或许还有其他情况),不能单纯标记,这样或许得到的不是最优解(可能箱子回到原来的位置才是最优)。人必须要先把箱子推走才可以进行下一步活动,这样的话在未来的某个状态下箱子或许会回到原来的位置。因此在标记状态时,可累计两次。
AC代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#define INF 0x3f3f3f3f
#define eps 1e-8
#define MAXN (300+10)
#define MAXM (100000)
#define Ri(a) scanf("%d", &a)
#define Rl(a) scanf("%lld", &a)
#define Rf(a) scanf("%lf", &a)
#define Rs(a) scanf("%s", a)
#define Pi(a) printf("%d\n", (a))
#define Pf(a) printf("%.2lf\n", (a))
#define Pl(a) printf("%lld\n", (a))
#define Ps(a) printf("%s\n", (a))
#define W(a) while(a--)
#define CLR(a, b) memset(a, (b), sizeof(a))
#define MOD 1000000007
#define LL long long
#define lson o<<1, l, mid
#define rson o<<1|1, mid+1, r
#define ll o<<1
#define rr o<<1|1
using namespace std;
int n, m;
int Map[10][10];
bool judge(int x, int y){
return x >= 0 && x < n && y >= 0 && y < m;
}
int vis[10][10];
bool use[10][10];
int Move[4][2] = {0,1, 0,-1, 1,0, -1,0};
bool DFS(int x, int y, int tx, int ty)
{
use[x][y] = true;
if(x == tx && y == ty)
return true;
for(int i = 0; i < 4; i++)
{
int xx = x + Move[i][0];
int yy = y + Move[i][1];
if(!judge(xx, yy) || Map[xx][yy] == 1 || use[xx][yy]) continue;
if(DFS(xx, yy, tx, ty)) return true;
}
return false;
}
struct Node
{
int x, y, step;
int mx, my;
};
int sx, sy, tx, ty;
int BFS(int x, int y)
{
queue<Node> Q; CLR(vis, 0);
Node now, next;
now.x = x; now.y = y; now.step = 0; vis[now.x][now.y] = 1;
now.mx = sx, now.my = sy; Q.push(now);
while(!Q.empty())
{
now = Q.front();
Q.pop();
if(Map[now.x][now.y] == 3)
return now.step;
for(int i = 0; i < 4; i++)
{
next.x = now.x + Move[i][0];//箱子下一步移动位置
next.y = now.y + Move[i][1];
if(!judge(next.x, next.y) || vis[next.x][next.y] > 2 || Map[next.x][next.y] == 1) continue;
int xx, yy;//人的目标位置,到这里才可以把箱子推到(next.x, next.y)
switch(i)
{
case 0: xx = now.x + Move[1][0]; yy = now.y + Move[1][1]; break;
case 1: xx = now.x + Move[0][0]; yy = now.y + Move[0][1]; break;
case 2: xx = now.x + Move[3][0]; yy = now.y + Move[3][1]; break;
case 3: xx = now.x + Move[2][0]; yy = now.y + Move[2][1]; break;
}
if(!judge(xx, yy) || Map[next.x][next.y] == 1) continue;
int t;
t = Map[now.x][now.y];
Map[now.x][now.y] = 1;
CLR(use, false);
if(DFS(now.mx, now.my, xx, yy))//人从上次停留的位置 能否 到达目标位置
{
vis[next.x][next.y]++;
next.step = now.step + 1;
next.mx = now.x;
next.my = now.y;
Q.push(next);
}
Map[now.x][now.y] = t;
}
}
return -1;
}
int main()
{
int t; Ri(t);
W(t)
{
Ri(n); Ri(m);
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
Ri(Map[i][j]);
if(Map[i][j] == 4)
{
sx = i;
sy = j;
}
if(Map[i][j] == 2)
{
tx = i;
ty = j;
}
}
}
Pi(BFS(tx, ty));
}
return 0;
}