石碑之谜:滚动机关

发布于:2024-05-16 ⋅ 阅读:(54) ⋅ 点赞:(0)

描述

在蒙德和璃月的边界地带,有一个被遗忘的神庙,里面有一个奇怪的机关:滚动石碑。小熊必须操作这个1×1×2的长方体石碑,使其通过不同的地面环境,最终放置到神秘的符号“O”上,以解开通往宝藏的大门。

石碑可以有三种放置形式:

  • 立在地面上(1×1的面接触地面)。
  • 横躺在地面上(1×2的面接触地面),似乎是为了休息或避开某些地面的损害。
  • 竖趟在地面上(1×2的面接触地面)

地图是一个N行M列的矩阵,代表着充满未知的遗迹内部:

  • 正常的地面由“.”表示,平坦且安全。
  • 易碎的地面用“E”标识,石碑不能以全部重量压在这种地面上,特别是立着时。
  • 不能通过的地面用“#”表示,这可能是古代结界或坍塌的部分。
  • 起点用“X”表示,石碑的初始位置。
  • 终点由“O”标识,是需要将石碑立在其上的目标位置。

小熊通过上下左右四个方向键来控制石碑沿边缘滚动90°。在整个探索过程中,石碑不能碰到不可通过的地面,也不能在易碎地面上以1×1的面立着。

地图上的“X”可能标识一个或两个相邻的位置,显示石碑是初始时是立着还是躺着的状态。

小熊的任务是以最少的步数,将石碑正确地立在“O”上,揭开这片古老土地的秘密。

image.png

image.png

image.png

输入

输入包含多组测试用例。

对于每个测试用例,第一行包括两个整数N和M。

接下来N行用来描述地图,每行包括M个字符,每个字符表示一块地面的具体状态。

当输入用例N=0,M=0时,表示输入终止,且该用例无需考虑。

数据范围

3≤N,M≤500

输出

对于每个测试用例,输出一个整数,表示小熊将石碑从起点推到目标点所需的最少步数。如果石碑无法成功地被推至目标点,则输出"Impossible"。

每个测试用例的结果输出在单独的一行。

输入样例 1 
7 7
#######
#..X###
#..##O#
#....E#
#....E#
#.....#
#######
0 0
输出样例:

10

代码实现:

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=510;
char area[N][N];
int dist[N][N][3];
int n,m;
struct stone
{//立0    横1    竖2
    int x,y,st;
    stone(int a,int b,int c){x=a,y=b,st=c;}
    stone():x(0),y(0),st(0){};
};
stone start,target;

int dxyz[4][3][3]={//dxyz[i][st][x,y,st]
    {{-2,0,2},{-1,0,1},{-1,0,0}},//向上
    {{0,-2,1},{0,-1,0},{0,-1,2}},//向左
    {{1,0,2},{1,0,1},{2,0,0}},//向下
    {{0,1,1},{0,2,0},{0,1,2}}//向右
    };
stone movestone(stone &p,int i)
{
    int x=p.x+dxyz[i][p.st][0];
    int y=p.y+dxyz[i][p.st][1];
    int z=dxyz[i][p.st][2];
    return stone(x,y,z);
}
bool isInside(int i,int j)
{
    return (i>=0&&j>=0&&i<n&&j<m);
}
bool isValid(stone s)
{
    if(!isInside(s.x,s.y)||area[s.x][s.y]=='#')return false;
    if(s.st==1&&(!isInside(s.x,s.y+1)||area[s.x][s.y+1]=='#'))return false;
    if(s.st==2&&(!isInside(s.x+1,s.y)||area[s.x+1][s.y]=='#'))return false;
    if(s.st==0&&area[s.x][s.y]=='E')return false;
    return true;
}
bool isVisited(stone t)
{
    return dist[t.x][t.y][t.st]!=-1;
}
int bfs(stone& s)
{
    queue<stone> q;
    q.push(s);
    dist[s.x][s.y][s.st]=0;
    while(q.size())
    {
        auto t=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            stone p=movestone(t,i);
            if(!isValid(p))continue;
            if(!isVisited(p))
            {
                dist[p.x][p.y][p.st]=dist[t.x][t.y][t.st]+1;
                q.push(p);
                if(p.x==target.x&&p.y==target.y&&p.st==target.st)
                    return dist[p.x][p.y][p.st];
            }
        }
    }
    return -1;
}
int main()
{
    while(cin>>n>>m&&n)
    {
        memset(area,'#',sizeof area);
        memset(dist,-1,sizeof dist);
        for(int i=0;i<n;i++)cin>>area[i];
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
        {
            char t=area[i][j];
            if(t=='X')
            {
                start.x=i,start.y=j,start.st=0;
                area[i][j]='.';
                if(area[i][j+1]=='X')start.st=1,area[i][j+1]='.';
                if(area[i+1][j]=='X')start.st=2,area[i+1][j]='.';
            }
            if(t=='O')target.x=i,target.y=j,target.st=0;
        }
        int res=bfs(start);
        if(res==-1)cout<<"Impossible"<<endl;
        else cout<<res<<endl;
    }
    return 0;
}