1. 程式人生 > >【1728 HDU】逃離迷宮

【1728 HDU】逃離迷宮

題目:點選開啟題目連結

Problem Description:

  給定一個m × n (m行, n列)的迷宮,迷宮中有兩個位置,gloria想從迷宮的一個位置走到另外一個位置,當然迷宮中有些地方是空地,gloria可以穿越,有些地方是障礙,她必須繞行,從迷宮的一個位置,只能走到與它相鄰的4個位置中,當然在行走過程中,gloria不能走到迷宮外面去。令人頭痛的是,gloria是個沒什麼方向感的人,因此,她在行走過程中,不能轉太多彎了,否則她會暈倒的。我們假定給定的兩個位置都是空地,初始時,gloria所面向的方向未定,她可以選擇4個方向的任何一個出發,而不算成一次轉彎。gloria能從一個位置走到另外一個位置嗎?

 Input:

  第1行為一個整數t (1 ≤ t ≤ 100),表示測試資料的個數,接下來為t組測試資料,每組測試資料中,
  第1行為兩個整數m, n (1 ≤ m, n ≤ 100),分別表示迷宮的行數和列數,接下來m行,每行包括n個字元,其中字元'.'表示該位置為空地,字元'*'表示該位置為障礙,輸入資料中只有這兩種字元,每組測試資料的最後一行為5個整數k, x1, y1, x2, y2 (1 ≤ k ≤ 10, 1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m),其中k表示gloria最多能轉的彎數,(x1, y1), (x2, y2)表示兩個位置,其中x1,x2對應列,y1, y2對應行。

Output:

  每組測試資料對應為一行,若gloria能從一個位置走到另外一個位置,輸出“yes”,否則輸出“no”。

Sample Input:

2
5 5
...**
*.**.
.....
.....
*....
1 1 1 1 3
5 5
...**
*.**.
.....
.....
*....
2 1 1 1 3

Sample  Output:

no
yes

題目思路:這道題跟我們平時做的不太一樣,平時都是用bfs套路求最少可以幾步走到目的地,而這道題要求的是從起點到終點的最少拐彎次數,所以這道題按老套路是做不出來的。這裡提供一種思路,因為要求拐彎次數,而如果你每走一步然後判斷它是否拐彎,拐彎就+1,沒有拐彎就不用加,的話比較麻煩,走一步判斷一步,而且你必須要通過3個點才能判斷它是否拐彎,即你要保留它上一步和它上上一步的座標,才能判斷它這一步是否拐彎,這樣的話腦瓜子就比較容易暈,還不一定能判斷對;那麼問題來了,如何判斷它是否拐彎了呢?你可以選定一個方向後,把這個方向走完(即把這個方向的點都遍歷完,直到碰到障礙物為止),用一個二維陣列標記每個位置,0代表這個位置的方向沒有走過,1代表這個位置的方向已經走過了,不用管它的,所以當碰到0時,就說明拐彎了,此時的拐彎次數就是上一個方向的拐彎次數+1。(由於起點的時候方向未定,4個方向都不算拐彎,因此假設起點時的拐彎次數為-1)

下面舉個栗子:(假設從S走到E,起點處的拐彎次數為-1,起初二維陣列vis中都標記為0,還沒開始走)

(為了方便,迷宮中未設定障礙物,起初的vis值我沒有標)

1.從起點的4個方向開始,當碰到標記為0時,就說明這是個新的方向,此時拐彎次數就需要+1,走過之後就標記為1,則從起點開始的4個方向的拐彎次數為0,如圖

                        

2.為了簡便起見,假設第2個點是從S的右邊第一個點,然後再從它開始對4個方向進行遍歷,然後碰到0就將它的拐彎次數+1,標記過的地方就不用再遍歷了,此時拐彎次數就變成了1,如圖

                       

3.重複上述做法,直到找到答案為止。

MY   DaiMa:

#include<iostream>
#include<queue>
#include<string.h>
#include<cstring>
#include<cstdio>
using namespace std;
char flag[105][105];///迷宮裡的字元
int t, n, m, k, dir[4][2] = { {0,-1},{0,1},{-1,0},{1,0} };///t組測試樣例,n行m列,dir4個方向
int vis[105][105];///用來判斷是否拐彎
struct position
{
    int x, y, time;///記錄time拐彎次數
} start, eend;
int bfs()
{
    queue<position> q;
    position cur,nex,rec;
    start.time = -1;
    flag[start.x][start.y] = '*';
    q.push(start);
    while(!q.empty())
    {
        cur = q.front();
        q.pop();
        if(cur.x == eend.x && cur.y == eend.y && cur.time <= k) return 1;
        for(int i = 0; i < 4 ;i++)
        {
            nex.x = cur.x + dir[i][0];
            nex.y = cur.y + dir[i][1];
            while((nex.x >= 0 && nex.x < n) && (nex.y >=0 && nex.y < m) && flag[nex.x][nex.y] != '*' )///將這個方向的點都遍歷完
            {
                if(vis[nex.x][nex.y] == 0)///未被標記過,說明這個方向是新的方向,此時拐彎次數需要+1
                {
                    nex.time = cur.time + 1;
                    vis[nex.x][nex.y] = 1;///走過的地方標記
                    q.push(nex);
                }
                rec.x = nex.x + dir[i][0];///rec存的是這個方向的下一個點的座標
                rec.y = nex.y + dir[i][1];
                nex = rec;
            }
        }
    }
    return -1;
}
int main()
{
    int x1,x2,y1,y2;
    cin >> t;
    while(t--)
    {
        cin >> n >> m;
        for(int i = 0; i < n ;i ++)
        {
            for(int j = 0; j < m; j++)
                cin >> flag[i][j];
        }
        cin >> k >> x1 >> y1 >> x2 >> y2;
        start.x = y1-1;
        start.y = x1-1;
        eend.x = y2-1;
        eend.y = x2-1;
        int ans;
        memset(vis,0,sizeof(vis));
        ans = bfs();
        if(ans == -1)
            cout << "no" << endl;
        else
            cout << "yes" << endl;
    }
}