bfs 與優先佇列————洛谷p1126(歷經兩個小時總算AC了,哭暈)

小明算法嘎嘎猛發表於2024-09-25

機器人搬重物

題目描述

機器人移動學會(RMI)現在正嘗試用機器人搬運物品。機器人的形狀是一個直徑 \(1.6\) 米的球。在試驗階段,機器人被用於在一個儲藏室中搬運貨物。儲藏室是一個 \(N\times M\) 的網格,有些格子為不可移動的障礙。機器人的中心總是在格點上,當然,機器人必須在最短的時間內把物品搬運到指定的地方。機器人接受的指令有:

  • 向前移動 \(1\) 步(Creep);
  • 向前移動 \(2\) 步(Walk);
  • 向前移動 \(3\) 步(Run);
  • 向左轉(Left);
  • 向右轉(Right)。

每個指令所需要的時間為 \(1\) 秒。請你計算一下機器人完成任務所需的最少時間。

輸入格式

第一行為兩個正整數 \(N,M\ (1\le N,M\le50)\),下面 \(N\) 行是儲藏室的構造,\(0\) 表示無障礙,\(1\) 表示有障礙,數字之間用一個空格隔開。接著一行有 \(4\) 個整數和 \(1\) 個大寫字母,分別為起始點和目標點左上角網格的行與列,起始時的面對方向(東 \(\tt E\),南 \(\tt S\),西 \(\tt W\),北 \(\tt N\)),數與數,數與字母之間均用一個空格隔開。終點的面向方向是任意的。

輸出格式

一個整數,表示機器人完成任務所需的最少時間。如果無法到達,輸出 \(-1\)

樣例 #1

樣例輸入 #1

9 10
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0 0 0
0 0 0 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 1 0
7 2 2 7 S

樣例輸出 #1

12

本題是一個很典型的dfs題,但坑點很多,一連坑了我好多次。。。。不嘻嘻

  • 首先就是計算轉向的時間,我將NSWE是個方向代數為1,2,3,4,然後取他們差的絕對值,代表轉向的時間,但是沒有注意到4和1相差3,但實際上只用1秒,這個錯純屬犯病。。。寫太快了.
  • 然後就是如何將輸入的方格圖轉化為格點圖,這個要畫出圖來
  • 之後就是機器人走三步的時候有可能跳過障礙,但這是不允許的,所以需要剪枝
  • 最後就是要用優先佇列優先取出tm最小的節點
  • 還有就是它測試用例的一些毒點,比如起點卡牆裡(@-@!
    具體細節在程式碼裡

程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<cmath>
#include<iomanip>
using namespace std;
int a[55][55]; //儲存方格圖
long long mp[55][55]; //儲存格點圖,即機器人行走的圖
int dis[55][55]; //用於記錄機器人到該格點的最短時間
int n, m;  //方格圖的尺寸
int x1, y11, x2, y2; //記錄起點終點座標
int sto;//起點的方向 
string ch;//讀入起點的方向 

struct node
{
    int x;
    int y;//當前點的座標 
    int to;//1=>N(上) 2=>E(右) 3=>S(下) 4=>W(左) 方向編號 
    int tm;//從起點到當前點的最短時間 
    bool operator < (node a)const {
        return tm > a.tm;
    }
    //stl中優先佇列預設優先取出最大值,所以要過載運算子,保證每次取出的都是代價最小的元素
};
priority_queue<node> que;//優先佇列
node now, nxt;

int dx[] = { 0, -1,0,1,0 };
int dy[] = { 0, 0,1,0,-1 };

int abs(int a, int b) {
    return a > b ? a - b : b - a;
}

int calchange(int now, int aft) {
    //注意這個情況
    if (abs(now, aft) == 3) {
        return 1;
    }
    else {
        return abs(now, aft);
    }
}

void readto()
{
    switch (ch[0])
    {
    case 'N': sto = 1; break;
    case 'S': sto = 3; break;
    case 'W': sto = 4; break;
    case 'E': sto = 2; break;
    }
    return;
}

void change()
{
    //把邊框全部置為1,因為機器人有寬度,不能出現在那裡
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= m; ++j)
        {
            if (a[i][j] == 1)//如果當前格為障礙物,則它的四個頂點都不能走 
            {
                mp[i + 1][j] = 1;
                mp[i][j + 1] = 1;
                mp[i + 1][j + 1] = 1;
                mp[i][j] = 1;
            }
            if (i == 1 || j == 1) {
                mp[i][j] = 1;
            }
            if (i == n || j == m) {
                mp[i + 1][j + 1] = 1;
            }
        }
    }
    mp[n + 1][1] = 1;
    mp[1][m + 1] = 1;
}

void bfs() {
    
    dis[x1+1][y11+1] = 0;
    node fir;
    fir.x = x1+1;
    fir.y = y11+1;
    fir.to = sto;
    fir.tm = 0;
    que.push(fir);
    int changecost = 0;
    while (!que.empty()) {
        now = que.top();
        //這個是列印路徑的程式碼
        //cout << now.x << " " << now.y << " " << now.to << " " << now.tm << endl;
        if (now.x == x2+1 && now.y == y2+1) break;  //剪枝
        que.pop();
        if (now.tm > dis[now.x][now.y]) continue;  //剪枝,這個可不加,好像沒用
        //遍歷四個方向
        for (int i = 1; i <= 4; i++) {
            changecost = calchange(now.to, i);
            //三種移動方式
            for (int j = 1; j <= 3; j++) {
                nxt.x = now.x + dx[i] * j;
                nxt.y = now.y + dy[i] * j;
                //if (mp[nxt.x][nxt.y] == 1 && j==1 || mp[nxt.x][nxt.y] == 1 && j == 2) break;
                if (nxt.x <= 1 || nxt.x > n || nxt.y <= 1 || nxt.y > m || mp[nxt.x][nxt.y] == 1) break;  //剪枝,這個邊界判斷最好對著圖來
                nxt.to = i;
                nxt.tm = now.tm + 1 + changecost;
                if (dis[nxt.x][nxt.y] > nxt.tm) {
                    dis[nxt.x][nxt.y] = nxt.tm;
                    que.push(nxt);
                }
            }
        }
    }
}
int main()
{
    memset(dis, 1061109567, sizeof(dis));
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= m; ++j)
        {
            scanf("%d", &a[i][j]);
        }
    }
    cin >> x1 >> y11 >> x2 >> y2;  //在c++中y1是個函式,所以要換一個
    cin >> ch;
    readto();//判斷ch代表的方向 
    change();//把方格地圖轉化為機器人可以走的格點地圖
    //特判一下,防止依賴就卡牆,或者終點卡牆,這個起點和終點的索引最好對著圖來
    if (mp[x1 + 1][y11 + 1] == 1 || mp[x2+1][y2+1]==1) {
        cout << -1;
        return 0;
    }
    bfs();
    //這個是列印格點圖的程式碼,可以列印出來看看
    /*for (int i = 1; i <= n + 1; i++) {
        for (int j = 1; j <= m + 1; j++) {
            cout << mp[i][j] << " ";
        }
        cout << endl;
    }*/
    if (dis[x2+1][y2+1] == 1061109567) {
        cout << -1;
    }
    else {
        cout << dis[x2 + 1][y2 + 1];
    }
}

相關文章