Problem地址:http://acm.hdu.edu.cn/showproblem.php?pid=1254
看完这到题,我就觉得题目有一点没有说明白:数字3,也就是目标点,人可不可以走。
事实证明,数字3是可以走的,所以在判断人是否能到达目的地时,要认为0和3都能走。
这是第一点。
接着才是我的思路:
首先,这是一道搜索题(这简直是废话)。题目要求计算箱子所需的最小步数,而不是人的。每次移动箱子时,就要考虑人能否到达箱子的后面,这样,箱子才能被往前推。其次,在判断人是否能到达箱子的后面时,除了我刚说的注意点外,还有一点是需要注意的,就是此时人不能走到箱子的位置。也就是每次移动箱子时,就要把现在箱子的位置标记为人不能通过,待判断完后,再取消刚才对箱子的标记。接着是考虑箱子能否往回走,实际上对于每一点时,箱子可重复走到,但从每个方向走到一个点时只能走一次。同一个点不能被箱子从同一个发向走两次,至于为什么,可以这么想:如果同一个点能被箱子从同一个发向走两次,那么箱子就必然可以第三次从这个方向走到这点,也自然可以第四次,第五次,第六次。。。。。。所以需要一个数组来进行判断箱子是不是已经从这个方向走过这个点了。
#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>
using namespace std;
const int MAXN = 10;
int maze[MAXN][MAXN]; // represents the inputing matrix
int M, N; //M rows, N columns
int dir[4][2] = { -1,0, 1,0, 0,-1, 0,1 }; // directions : up, down, left, right
bool boxVis[MAXN][MAXN][MAXN]; // row, column, direction
typedef struct point{
int x;
int y;
}point; // just used as a point
typedef struct node{
int steps;
int x, y;
int from;
point person;
friend bool operator < ( node t1, node t2 )
{
return t2.steps < t1.steps;
}
}node; // used for the condition of box
point target, startP, startBox;
bool Is_reach( point pS, point pF ) // person start, person finish
{
queue <point> q;
bool hash[M][N];
memset( hash, false, sizeof(hash) );
point t, next;
t.x = pS.x;
t.y = pS.y;
hash[t.x][t.y] = true; // the start point
q.push( t );
int i;
while( !q.empty() )
{
t = q.front();
q.pop();
if( t.x==pF.x && t.y==pF.y )
return true;
for( i=0;i<4;i++ )
{
next.x = t.x + dir[i][0];
next.y = t.y + dir[i][1];
if( next.x>=0 && next.x<M && next.y>=0 && next.y<N
&& (maze[next.x][next.y]==0||maze[next.x][next.y]==3) // 推箱子的人可以走0(地板),也可以走3(目的地)
&& !hash[next.x][next.y] ) // 0 means it is empty
{
hash[next.x][next.y] = true;
q.push(next);
}
}
}
return false; // if the person can't reach, return false
}
int BoxBfs()
{
priority_queue <node> q;
node t, next;
t.x = startBox.x;
t.y = startBox.y;
t.steps = 0;
t.person.x = startP.x;
t.person.y = startP.y;
q.push( t );
int record;
int i;
point pF; // 推箱子的人需要到的地方
memset( boxVis, false, sizeof(boxVis) );
while( !q.empty() )
{
t = q.top();
q.pop();
if( t.x==target.x && t.y==target.y )
return t.steps;
for( i=0;i<4;i++ )
{
next.x = t.x + dir[i][0];
next.y = t.y + dir[i][1];
pF.x = t.x - dir[i][0];
pF.y = t.y - dir[i][1]; // 推箱子的人需要到的地方
if( next.x>=0 && next.x<M && next.y>=0 && next.y<N && !boxVis[next.x][next.y][i]
&& maze[next.x][next.y]!=1)
{
record = maze[t.x][t.y];
maze[t.x][t.y] = 2; // 标记, 使箱子现在所在的位置不能被人所走过
if( Is_reach( t.person, pF ) )
{
next.steps = t.steps + 1;
next.person.x = t.x;
next.person.y = t.y;
q.push( next );
boxVis[next.x][next.y][i] = true;
}
maze[t.x][t.y] = record;
}
}
}
return -1;
}
int main()
{
int T;
int i,j;
cin >> T;
while( T-- )
{
scanf("%d%d", &M, &N);
memset( boxVis, -1, sizeof(boxVis) );
for( i=0;i<M;i++ )
{
for( j=0;j<N;j++ )
{
scanf("%d", &maze[i][j]);
switch (maze[i][j]){
case 2: startBox.x = i; startBox.y = j;
maze[i][j] = 0; // avoiding that this point becomes a disreached point
break;
case 3: target.x = i; target.y = j;
//maze[i][j] = 0;
break;
case 4: startP.x = i; startP.y = j;
maze[i][j] = 0; // the reason is same with the reason before
break;
};
}
}
cout << BoxBfs() << endl;
}
return 0;
}
/*
test data:
3 5
4 0 0 0 0
1 2 0 0 0
0 0 0 0 3
7 4
0 0 0 0
0 0 1 0
0 2 0 3
1 4 1 0
1 0 1 0
1 0 1 0
1 0 0 0
*/