POJ 2049 Finding Nemo

/*

做完這題我打算看《海底總動員》Demo Demo好可愛
用廣搜過的, 主要思路如下:
(1)首先是建圖, 由於輸入給的都是線段, 但是我們平常處理這類問題都是轉換爲網格來做的, 因此需要
將線段轉換爲網格.轉化的方法是對於每個格子,用其左上角點的座標來表示這個格子,如果其左上角點的
座標是[i][j],那麼這個格子就表示爲[i][j].將其四周邊界的四條線段歸這個格子管.即爲每個格子建一
個數組round[i][j][4],第三維的四個位置分別表示這四條線段的類型: 0表示空氣,1表示牆,2表示是一扇
門,這樣這個模型就建立好了.
(2)其次是bfs的起始點選爲Nemo所在的位置,注意要將這個浮點點位置轉化爲格子的座標.轉化的方法很簡
單.對於x座標取整即可,對於y座標取整+1即可,比如Nemo所在位置爲[1.2, 1.3]那麼轉化爲格子的座標即爲:
[1, 2].這個座標即位bfs遍歷的起始點
(3)遍歷的時候如果所走的方向是牆,則不可走.如果是門則將當前總的steps數+1,如果爲空氣,steps數不變.
另外一點就是如何判重.判重不能簡單地記錄有沒有被訪問過,而是需要記錄到當前格子的最小步驟.如果當
前總步驟數小於當前格子的最小步驟數,則更新其最小步驟數並將這個格子加入隊列中.
(4)遍歷的終止位置即爲題目中的出發位置[0, 0]
*/

#include <iostream>
#include <queue>
#define MAX_N 210 //最大限度邊界

using namespace std;

int v[MAX_N + 1][MAX_N + 1];            //v[i][j]表示到格子[i][j]的最小步驟數
int round[MAX_N + 1][MAX_N + 1][4];     //記錄當前格子四面邊界的類型, 0:air 1:wall 2:door

int wn, dn, startXI, startYI, minSteps; //wn:牆的數目,dn:門的數目,起始點對應的格子座標
double startXF, startYF;                //起始點的輸入浮點座標
                                       
int dirarray[4][2] = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}}; //方向數組,走四個方向對座標的變化
//上:0, 下:1, 左:2, 右:3

//入bfs隊列的元素類型
struct elem
{
    //x, y記錄這個格子的座標; dir記錄是從當前格子的哪個方向進入這個格子的,上:0, 下:1, 左:2, 右:3
    int x, y, dir, stepnum;
    //stepnum記錄到達當前格子所需的步驟數
};

queue<elem> bfsq; //bfs的隊列

//取當前方向的對面方向
void changeDir(int orgignal, int &newDir)
{
    if(orgignal == 0) newDir = 1;
    else if(orgignal == 1) newDir = 1;
    else if(orgignal == 2) newDir = 3;
    else newDir = 2;
}

//當斷當前座標是否在合法範圍內
bool inRange(int x, int y)
{
    return x >= 0 && x <= 205 && y >= 0 && y <= 205;
}

void bfs()
{
    //將Demo的位置入隊列作爲bfs的起始位置
    while(!bfsq.empty()) bfsq.pop();
    elem curelem, newelem;
    curelem.x = startXI; curelem.y = startYI; curelem.dir = -1; curelem.stepnum = 0;
    v[startXI][startYI] = 0;
    bfsq.push(curelem);

    int curx, cury, curdir, cursteps, newx, newy, newdir, newsteps, d;
    while(!bfsq.empty())
    {
        curelem = bfsq.front();
        bfsq.pop();
       
        curx = curelem.x; cury = curelem.y; curdir = curelem.dir; cursteps = curelem.stepnum;

        //到達出發點
        if(curx == 0 && cury == 0)
        {
            //更新所需位置的最優值
            if(cursteps < minSteps)
                minSteps = cursteps;
            continue;   
        }

        //遍歷當前格子的四個方向,嘗試往這四個方向走
        for(d = 0; d < 4; d++)
        {
            //不能往回走
            if(d != curdir)
            {
                //所走方向不能是牆
                if(round[curx][cury][d] != 1)
                {
                    //得到新的格子座標
                    newx = curx + dirarray[d][0];
                    newy = cury + dirarray[d][1];
                   
                    //新座標在合法範圍內
                    if(inRange(newx, newy))
                    {
                        //計算所有方向相對目標格子所在的方位
                        changeDir(d, newdir);

                        //門,步驟數+1
                        if(round[curx][cury][d] == 2)
                            newsteps = cursteps + 1;
                        else //空氣,步驟數不變
                            newsteps = cursteps;

                        //判斷這個新格子的新狀態是否需要入隊列
                        if((v[newx][newy] == 0xbf || newsteps < v[newx][newy]) && newsteps < minSteps)
                        {
                            v[newx][newy] = newsteps;
                            newelem.x = newx; newelem.y = newy; newelem.stepnum = newsteps; newelem.dir = newdir;
                            bfsq.push(newelem);
                        }
                    }
                }
            }
        }
    }
}

int main()
{
    int i, j, x, y, d, t;
    while(scanf("%d%d", &wn, &dn) && !(wn == -1 && dn == -1))
    {
        minSteps = INT_MAX;
        memset(v, 12, sizeof(v));
        memset(round, 0, sizeof(round));
        for(i = 1; i <= wn; i++)
        {
            scanf("%d%d%d%d", &x, &y, &d, &t);
            //輸入的預處理,將線段(牆)轉換爲相應格子對應的四面邊界
            if(d == 1)
                for(j = y + 1; j <= y + t; j++)
                    round[x][j][2] = round[x - 1][j][3] = 1;
            else
                for(j = x; j < x + t; j++)
                    round[j][y][0] = round[j][y + 1][1] = 1;
        }
        for(i = 1; i <= dn; i++)
        {
            scanf("%d%d%d", &x, &y, &d);
            //輸入的預處理,將線段(門)轉換爲相應格子的四面邊界方向
            if(d == 1)
                round[x][y + 1][2] = round[x - 1][y + 1][3] = 2;
            else
                round[x][y][0] = round[x][y + 1][1] = 2;
        }
        scanf("%lf%lf", &startXF, &startYF);
        //將Demo的位置轉換爲格子座標
        startXI = startXF;
        startYI = startYF + 1;

        //題目中的異常數據
        if(startXI < 0 || startXI > 199 || startYI < 0 || startYI > 199)
            printf("0/n");
        else
        {
            bfs();
            if(minSteps == INT_MAX) printf("-1/n");
            else printf("%d/n", minSteps);
        }
    }
    return 0;
}

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