只是记录一下自己的学习过程
题目描述
推箱子是一款经典游戏。这里我们玩的是一个简单版本,就是在一个N*M的地图上,有1个玩家、1个箱子、1个目的地以及若干障碍,其余是空地。玩家可以往上下左右4个方向移动,但是不能移动出地图或者移动到障碍里去。如果往这个方向移动推到了箱子,箱子也会按这个方向移动一格,当然,箱子也不能被推出地图或推到障碍里。当箱子被推到目的地以后,游戏目标达成。现在告诉你游戏开始是初始的地图布局,请问玩家至少要多少步才能达成目标?
输入
第一行输入两个数字N,M表示地图的大小。其中0<N,M<=12。
接下来有N行,每行包含M个字符表示游戏地图。其中 . 表示空地、X表示玩家、*表示箱子、#表示障碍、@表示目的地。
输出
输出一个数字表示玩家最少需要移动多少步才能将游戏目标达成。当无论如何达成不了的时候,输出-1。
样例输入
6 6
…#…
…
#*##…
…##.#
…X…
.@#…
样例输出
11
我的简要分析
写这个题目之前写过一个广度优先的跳马问题,跟这个题目差不多,不过障碍的判断不太一样,加上这里有个箱子需要考虑。
如果单纯的用一个二维数组标记人走过的位置,那箱子移动之后人就无法返回,但是实际上推动箱子之后人是可以返回的,所以当箱子移动后,标记人走过位置的数组需要进行重置,相当于箱子的位置跟标记人走过位置的数组是捆绑起来的,所以人走过的位置可以用四维数组进行标记:下标为人目前的位置以及箱子目前的位置。
关于广度优先探索(BFS)推荐这篇博客 链接.
详细代码
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
using namespace std;
char zmap[20][20]; //地图
int jmap[20][20][20][20] = { 0 }; //标记步数以及位置是否走过
int n1, m1; //终点位置
int n2, m2; //起点位置
int n3, m3; //箱子位置
int n, m; //地图大小
int people[4][2] = //人走的位置变化
{
{-1,0},
{0,-1},
{1,0},
{0,1},
};
//队列
typedef struct Sqeue
{
int* data;
int top;
int rear;
}Sqlist;
//队列初始化
void Lcreat(Sqlist* L)
{
L->data = new int[1000];
L->top = 0;
L->rear = 0;
}
//入队
void Lint(Sqlist* L, int e)
{
L->data[L->rear++] = e;
}
//出队
int Lout(Sqlist* L)
{
int e;
e = L->data[L->top++];
return e;
}
//判断队空
int Lempty(Sqlist* L)
{
return L->top == L->rear;
}
//判断要走的位置是否符合规定
int OutMap(int a, int b)
{
if (a >= 0 && a < n && b >= 0 && b < m && zmap[a][b] != '#')
return 0;
else
return 1;
}
//广度优先碳素
int venture()
{
//创建队列并将第一组数据输入,同时标记此位置
Sqlist L;
Lcreat(&L);
int p1, p2, x1, x2;
Lint(&L, n2);
Lint(&L, m2);
Lint(&L, n3);
Lint(&L, m3);
jmap[n2][m2][n3][m3] = 1;
//直到队空才停止探索
while (!Lempty(&L))
{
//输出队中第一组数据,若此时箱子位置等于终点位置则返回步数
p1 = Lout(&L);
p2 = Lout(&L);
x1 = Lout(&L);
x2 = Lout(&L);
if (x1 == n1 && x2 == m1)
return jmap[p1][p2][x1][x2] - 1;
//分别判断此时位置的四个方向是否可走
for (int i = 0; i < 4; i++)
{
//若此方向可走
if (!OutMap(p1 + people[i][0], p2 + people[i][1]))
{
//若此方向到达位置与箱子重合
if (p1 + people[i][0] == x1 && p2 + people[i][1] == x2)
{
//若箱子可以往此方向推进
if (!OutMap(x1 + people[i][0], x2 + people[i][1]))
{
//若此方向(箱子位置改变)到达位置没有走过
if (!jmap[p1 + people[i][0]][p2 + people[i][1]][x1 + people[i][0]][x2 + people[i][1]]) //判断要走的位置是否已经走过
{
//标记走过并且步数在上一个位置的基础上加1
jmap[p1 + people[i][0]][p2 + people[i][1]][x1 + people[i][0]][x2 + people[i][1]] = jmap[p1][p2][x1][x2] + 1;
//入队这一组数据(人和箱子都往此方向前进)
Lint(&L, p1 + people[i][0]);
Lint(&L, p2 + people[i][1]);
Lint(&L, x1 + people[i][0]);
Lint(&L, x2 + people[i][1]);
}
}
}
//若此方向到达位置没有跟箱子重合
else
{
//若此方向到达位置没有走过
if (!jmap[p1 + people[i][0]][p2 + people[i][1]][x1][x2])
{
//标记走过并且步数在上一个位置的基础上加1
jmap[p1 + people[i][0]][p2 + people[i][1]][x1][x2] = jmap[p1][p2][x1][x2] + 1;
//入队这一组数据(只有人往此方向前进)
Lint(&L, p1 + people[i][0]);
Lint(&L, p2 + people[i][1]);
Lint(&L, x1);
Lint(&L, x2);
}
}
}
}
}
//无法到达终点
return -1;
}
//主函数
int main()
{
//输入地图大小
cin >> n >> m;
//输入元素并标记起点、终点和箱子的位置
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
//地图元素
cin >> zmap[i][j];
//终点
if (zmap[i][j] == '@')
{
n1 = i; m1 = j;
}
//起点
if (zmap[i][j] == 'X')
{
n2 = i; m2 = j;
}
//箱子
if (zmap[i][j] == '*')
{
n3 = i; m3 = j;
}
}
//计算并输出所需步数
int k;
k = venture();
cout << k;
}