POJ3322bloxorz

这道题目很适合学习广度优先搜搜的小盆友:

 

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。

最难的其实是边界处理 以及木块的状态处理:
最好自己敲一遍代码:
 

/*
每个状态对应三个限制条件,x坐标、y坐标、lie
lie=0表示直立在(x,y)
lie=1表示横着躺,左半边在(x,y)
lie=2表示竖着躺,上半边在(x,y)
硬地(用.表示)
易碎地面(用E表示)
禁地(用#表示)
*/
# 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的新值
/*
lie=0的时候直立状态  ->朝左、右、上、下四个方向 lie分别变为 横趟、横趟、竖躺、竖躺
lie=1的时候横着趟状态->朝左、右、上、下四个方向 lie分别变为 直立、直立、横趟、横趟
lie=2的时候竖着躺状态->朝左、右、上、下四个方向 iie分别变为 竖趟、竖趟、直立、直立
*/
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;
    }
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值