这道题目很适合学习广度优先搜搜的小盆友:
Bloxorz是一个风靡世界的小游戏。Bloxorz的地图是一个N行M列的矩阵,每个位置可能是硬地(用.表示)、易碎地面(用E表示)、禁地(用#表示)、起点(用X表示)或终点(用O表示)。你的任务是操作一个1*1*2的长方体。这个长方体在地面上有两种放置形式,“立”在地面上(1*1的面接触地面)或者“躺”在地面上(1*2的面接触地面)。在每一步操作中,可以按上下左右四个键之一。按下之后,长方体向对应的方向沿着棱滚动90度。任意时刻,长方体不能有任何部位接触禁地(否则就会掉下去),并且不能立在易碎地面上(否则会因为压强太大掉下去)。X标识长方体的起始位置,地图上可能有一个X或者两个相邻的X。地图上唯一的一个O标识目标位置。求把长方体移动到目标位置(即立在O上)所需要的最少步数。如果无解,输出Impossible。在移动过程中,X和O标识的位置都可以看作是硬地被利用,3<=N,M<=500。
最难的其实是便觉处理 以及木块的状态处理:
最好自己敲一遍代码:
# include<cstdio>
# include <cstring>
# include<iostream>
# include<algorithm>
# include<cmath>
# include<queue>
using namespace std;
struct rec
{
int x, y, states;
//x,y表示横纵坐标
//lie表示状态
};
char s[510][510];//存储地图
rec st, ed;
int n, m, d[510][510][3];
queue<rec> q;//普通队列q
const int dx[4] = { 0,0,-1,1 }, dy[4] = { -1,1,0,0 };//竖着.竖着.横着.横着
bool valid(int x, int y)//地图的正常范围
{
return x>=1&&y>=1&&x<=n&&y<=m;
}
void parse_st_ed()//对起点和终点的处理
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (s[i][j] == 'O')//终点
{
ed.x = i;
ed.y = j;
ed.states = 0;//记录终点的坐标(ed.x,ed.y,直立)
s[i][j] = '.';//修改终点 方便搜索
}
else if (s[i][j] == 'X')//如果是起始位置x
{
for (int k = 0; k <4; k++)
{
int x = i + dx[k], y = j + dy[k];
if (valid(x, y) && s[x][y] == 'X')//躺着
{
st.x = min(i, x);//找到最左上角的坐标 当做起始坐标
st.y = min(j, y);
st.states = k < 2 ? 1 : 2;//竖着趟 k=1 横着趟 k=2
s[i][j] = s[x][y] = '.';//修改起始位置 方便搜索
break;
}
}
if (s[i][j] == 'X')//如果是直立状态
{
st.x = i;
st.y = j;
st.states = 0;//直接记录当前坐标
}
}
}
}
}
bool valid2(rec next)//判断next状态是否合法
{
if (!valid(next.x, next.y))//如果是超界 返回0
return 0;
if (s[next.x][next.y] == '#')//如果是禁地
return 0;
if (next.states == 0 && s[next.x][next.y] != '.')//如果站立但是当前位置不是硬地
return 0;
if (next.states == 1 && s[next.x][next.y + 1] == '#')
return 0;//如果是竖着躺但是当前位置的下方是禁地
if (next.states == 2 && s[next.x + 1][next.y] == '#')
return 0;//如果是横趟 但是右边位置是禁地
return 1;//否则OK 当前位置可以拓展
}
const int next_x[3][4] = { {0,0,-2,1},{0,0,-1,1},{0,0,-1,2} };
//next_x[i][j]表示lie=i时的朝j方向滚动后的x的坐标的状态
const int next_y[3][4] = { {-2,1,0,0},{-1,2,0,0},{-1,1,0,0} };
//next_y[i][j]表示lie=i时的朝j方向滚动后的y的坐标的状态
const int next_states[3][4] = { {1,1,2,2},{0,0,1,1},{2,2,0,0} };
//next_states[i][j]表示lie=i时的朝j方向滚动后的lie的新值
int bfs()
{
memset(d, -1, sizeof(d));//距离
while (q.size())q.pop();//清空队列
d[st.x][st.y][st.states] = 0;//初始化出发位置
q.push(st);//将起始位置加入队列
while (q.size())//进行拓展
{
rec now = q.front();1
q.pop();//取出当前需要拓展的状态
for (int i = 0; i < 4; i++)//四个方向进行拓展
{
rec next;//缓存下个状态 方便状态的变化处理
next.x = now.x + next_x[now.states][i];
next.y = now.y + next_y[now.states][i];
next.states = next_states[now.states][i];
//对当前的木块的状态 进行拓展
if (!valid2(next))//如果next状态非法
continue;
if (d[next.x][next.y][next.states] == -1)//还没有访问过
{
d[next.x][next.y][next.states] = d[now.x][now.y][now.states] + 1;
q.push(next);
//更新距离 并加入队列拓展
if (next.x == ed.x && next.y == ed.y && next.states == ed.states)
return d[next.x][next.y][next.states];//到达目标状态
//结束搜索 直接返回
}
}
}
return -1;//无解
}
int main()
{
while (cin >> n >> m && n)//地图
{
//getchar();
for (int i = 1; i <= n; i++)//
{
scanf("%s", s[i] + 1);//地图
}
parse_st_ed();//记录初始坐标
int ans = bfs();//广度优先搜索
if (ans == -1)//如果不可以
{
puts("Impossible");
}
else
cout << ans << endl;
}
}