2018.3.19
此题我用了广度优先搜索(BFS)的方法.广度优先搜索不仅仅可以使用在图的遍历中,而且可以用于求解复杂问题的最优解。
11
本题就是利用BFS的经典题目,首先来分析这个问题:
查找空间:所有(0,0,0)到点(A-1,B-1,C-1)合法的行走路径
查找目标:找到通往出口的最短路径
查找方法:将人物的三维位置坐标以及所花费的时间作为一个四元组(x,y,z,t),这就把查找所有路径转化成了对状态的搜索,即查找(A-1,B-1,C-1,t),对于具体的某一状态,可以通过扩展状态来遍历查找所有之后的状态,比如(x,y,z,t)在没有障碍物的情况下可以扩展为(x+1,y,z,t+1)(x-1,y,z,t+1)(x,y+1,z,t+1)(x,y-1,z,t+1)(x,y,z+1,t+1)(x,y,z-1,t+1)这六个状态,所以我们可以想象从(0,0,0,0)扩展到末状态的情形。
这种扩展的形式抽象成数据结构就是树,由(0,0,0,0) 为根节点,在理想状态下可以生出6个孩子,分别为(x+1,y,z,t+1)(x-1,y,z,t+1)(x,y+1,z,t+1)(x,y-1,z,t+1)(x,y,z+1,t+1)(x,y,z-1,t+1),而这6个孩子又有机会扩展生出6*6个,如此延续,而我们的任务就是按层次遍历这棵树,这样就可以尽可能早的找到目标状态。
对于扩展,我们需要运用一些技巧来剪支,比如遇到障碍的支路需要删除,之前访问的过的位置在后来访问时必然不可能是最短路径的组成部分,这也可删除,这可以用一个bool类型的数组来记录其是否已经进行过
#include
#include
using namespace std;
bool mark[50][50][50] ;
int maze[50][50][50] ;
struct Pos
{
int x,y,z ;
int t ;
};
queue Q ;
int go[][3] =
{
1 , 0 , 0 ,
-1 , 0 , 0 ,
0 , 1 , 0 ,
0 ,-1 , 0 ,
0 , 0 , 1 ,
0 , 0 ,-1
};
int BFS(int a , int b ,int c)
{
while(Q.empty() == false)
{
Pos now = Q.front();
Q.pop();
for(int i = 0 ; i < 6 ; i++)
{
int nx = now.x + go[i][0];
int ny = now.y + go[i][1];
int nz = now.z + go[i][2];
//如果跑到范围外面去了
if(nx < 0 || ny < 0 || nz < 0 || nx >= a || ny >= b || nz >= z) continue ;
//如果有障碍物
if(maze[nx][ny][nz] == 1) continue ;
//如果之前已经来过这个点了
if(mark[nx][ny][nz] == true) continue ;
Pos temp ;
temp.x = nx ;
temp.y = ny ;
temp.z = nz ;
temp.t = now.t + 1 ;
Q.push(temp);
mark[nx][ny][nz] = true ;
if(nx == a-1 && ny == b-1 && nz == c-1)
return temp.t ;
}
}
return -1 ;
}
int main()
{
int T ;
scanf("%d",&T);
while(T--)
{
int a,b,c,t ;
scanf("%d%d%d%d",&a,&b,&c,&t);
for(int i = 0 ; i < a ; i++)
{
for(int j = 0 ; j < b ; j++)
{
for(int k = 0 ; k < c ; k++ )
{
scanf("%d",&maze[i][j][k]);
mark[i][j][k] = false ;
}
}
}
//由于Q开始不一定为空,所以要置空
while(Q.empty() == false) Q.pop();
mark[0][0][0] = true ;
Pos start ;
start.x = start.y = start.z =start.t = 0 ;
Q.push(start);
int ans = BFS(a,b,c);
if(ans <= t) printf("%d\n",ans);
else printf("-1\n");
}
return 0 ;
}
那么对于推箱子,思考方式也类似,只不过有一点需要注意,由于推箱子里有两个重要的元素,人和箱子,所以要mark人和箱子的坐标,mark[x1][y1][x2][y2],一开始只mark[x1][y1]了人的坐标信息这样会导致错误。
#include
#include
using namespace std ;
bool mark[8][8][8][8] ;
char game[8][8];
int go[][2] =
{
1 , 0 ,
-1, 0 ,
0 , 1 ,
0 ,-1
};
struct Pos
{
int x1 , y1 ,x2 ,y2 ;
int t ;
};
queue Q ;
int BFS(int N , int M)
{
while(Q.empty() == false)
{
Pos now = Q.front();
Q.pop();
for(int i = 0 ; i < 4 ; i++)
{
int nx = now.x1 + go[i][0] ;
int ny = now.y1 + go[i][1] ;
if(nx < 0 || nx >= N || ny < 0 || ny >= M ) continue ;
if(game[nx][ny] == '#' ) continue ;
//到这里说明人可以走到这个点,下面就判断人是否和箱子有重合
if( nx == now.x2 && ny == now.y2)
{
//推算箱子的坐标
int nxx = nx + go[i][0] ;
int nyy = ny + go[i][1] ;
//如果1 越界 2 障碍 3 已访问
if( nxx < 0 || nxx >= N || nyy < 0 || nyy >= M) continue ;
if( game[nxx][nyy] == '#' ) continue ;
if( mark[nx][ny][nxx][nyy] == true ) continue ;
//否则
if( game[nxx][nyy] == '@') return now.t + 1 ;
Pos temp ;
temp.x1 = nx ;
temp.y1 = ny ;
temp.x2 = nxx ;
temp.y2 = nyy ;
temp.t = now.t + 1 ;
Q.push(temp);
mark[nx][ny][nxx][nyy] = true ;
}
//人没有接触到箱子
else
{
int nxx = now.x2 ;
int nyy = now.y2 ;
if( mark[nx][ny][nxx][nyy] == true ) continue ;
Pos temp ;
temp.x1 = nx ;
temp.y1 = ny ;
temp.x2 = nxx ;
temp.y2 = nyy ;
temp.t = now.t + 1 ;
mark[nx][ny][nxx][nyy] = true ;
Q.push(temp);
}
}
}
return -1 ;
}
int main()
{
for(int i = 0 ; i < 8 ; i++)
for(int j = 0 ; j < 8 ; j++)
for(int k = 0 ; k < 8 ; k++)
for(int l = 0 ; l < 8 ; l++)
mark[i][j][k][l] = false;
int N , M ,px , py , qx , qy;
cin>> N >> M ;
for(int i = 0 ; i < N ; i++)
{
for(int j = 0 ; j < M ; j++)
{
cin >> game[i][j] ;
//记录人的位置
if(game[i][j] == 'X')
{
px = i ;
py = j ;
}
//记录箱子的位置
else if(game[i][j] == '*')
{
qx = i ;
qy = j ;
}
}
}
//初始状态设置true
mark[px][py][qx][qy] = true ;
Pos start ;
start.x1 = px ;
start.y1 = py ;
start.x2 = qx ;
start.y2 = qy ;
start.t = 0 ;
while(Q.empty() == false ) Q.pop();
Q.push(start);
int ans = BFS(N,M);
cout << ans ;
return 0 ;
}