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;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章