P1126 机器人搬重物

题目描述

机器人移动学会(RMI)现在正尝试用机器人搬运物品。机器人的形状是一个直径 米的球。在试验阶段,机器人被用于在一个储藏室中搬运货物。储藏室是一个 的网格,有些格子为不可移动的障碍。机器人的中心总是在格点上,当然,机器人必须在最短的时间内把物品搬运到指定的地方。机器人接受的指令有:

  • 向前移动 步(Creep);
  • 向前移动 步(Walk);
  • 向前移动 步(Run);
  • 向左转(Left);
  • 向右转(Right)。

每个指令所需要的时间为 秒。请你计算一下机器人完成任务所需的最少时间。

输入格式

第一行为两个正整数 ,下面 行是储藏室的构造, 表示无障碍, 表示有障碍,数字之间用一个空格隔开。接着一行有 个整数和 个大写字母,分别为起始点和目标点左上角网格的行与列,起始时的面对方向(东 ,南 ,西 ,北 ),数与数,数与字母之间均用一个空格隔开。终点的面向方向是任意的。

输出格式

一个整数,表示机器人完成任务所需的最少时间。如果无法到达,输出 。

输入输出样例 #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

解释

难点一:格点 vs 格子(坐标系处理)

题目输入的是N×M的方格(Cells),有障碍物的地方是 1。
但是,题目说机器人是走在**格点(Intersections/Grid lines)**上的。

N行M列的格子,实际上只有(N − 1) × (M − 1)个可走的内部交叉点。

边界判断:nx < 1 || nx >= N || ny < 1 || ny >= M (有效坐标范围是 1 到 N − 1,1 到 M − 1)。

难点二:胖机器人(预处理地图)

机器人是有体积的(占 2×2 个格子)。
如果把机器人看作一个质点(比如看作机器人的左上角坐标 (r,c)),那么只有当该点右下方的 4 个格子[(r,c),(r,c + 1),(r + 1,c),(r + 1,c + 1)全都是空地(0)]时,这个点才是合法的。

难点三:状态定义(三维数组)

普通的迷宫只需要记录 visited[x][y]。
但这道题中,“在 (x,y) 面向北”和“在 (x,y) 面向东”是两种完全不同的状态。因为转向需要消耗时间,且不同朝向能去的下一步不同。

AC代码

#include<bits/stdc++.h>
using namespace std;
 
struct Pos
{
    int x;
    int y;
    int Dir;
    int time;
};
 
int N,M;
constexpr int MAXNUM = 55;
vector<vector<int>> Grid;
vector<vector<bool>> CanGo;
int visited[MAXNUM][MAXNUM][4];
int Sx,Sy,Ex,Ey;
int sDir;
 
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
 
int bfs()
{
    queue<Pos> Q;
    Q.push({Sx,Sy,sDir,0});
    visited[Sx][Sy][sDir] = 0;
    while (!Q.empty())
    {
        Pos cur = Q.front();
        Q.pop();
 
        if (cur.x == Ex && cur.y == Ey)
        {
            return cur.time;
        }
 
        // 直行
        for (int i = 1;i <= 3;i++)
        {
            int nx = cur.x + dx[cur.Dir] * i;
            int ny = cur.y + dy[cur.Dir] * i;
 
            if (nx < 1 || nx >= N || ny < 1 || ny >= M)
            {
                break;
            }
 
            if (!CanGo[nx][ny])
            {
                break;
            }
 
            if (visited[nx][ny][cur.Dir] == -1)
            {
                visited[nx][ny][cur.Dir] = cur.time + 1;
                Q.push({nx,ny,cur.Dir,cur.time + 1});
            }
        }
 
        // 左转
        int leftDir = (cur.Dir + 3) % 4;
        if (visited[cur.x][cur.y][leftDir] == -1)
        {
            visited[cur.x][cur.y][leftDir] = cur.time + 1;
            Q.push({cur.x,cur.y,leftDir,cur.time + 1});
        }
 
        // 右转
        int rightDir = (cur.Dir + 1) % 4;
        if (visited[cur.x][cur.y][rightDir] == -1)
        {
            visited[cur.x][cur.y][rightDir] = cur.time + 1;
            Q.push({cur.x,cur.y,rightDir,cur.time + 1});
        }
    }
 
    return -1;
}
 
int main()
{
    cin >> N >> M;
    Grid.resize(N + 1,vector<int>(M + 1));
    CanGo.resize(N + 1,vector<bool>(M + 1));
 
    for (int i = 1;i <= N;i++)
    {
        for (int j = 1;j <= M;j++)
        {
            cin >> Grid[i][j];
        }
    }
 
    for (int i = 1;i < N;i++)
    {
        for (int j = 1;j < M;j++)
        {
            if (Grid[i][j] == 0 && Grid[i][j + 1] == 0 && Grid[i + 1][j] == 0 && Grid[i + 1][j + 1] == 0)
            {
                CanGo[i][j] = true;
            }
        }
    }
 
    char SDir;
    cin >> Sx >> Sy >> Ex >> Ey >> SDir;
    switch (SDir)
    {
        case 'N': sDir = 0; break;
        case 'E': sDir = 1; break;
        case 'S': sDir = 2; break;
        case 'W': sDir = 3; break;
        default: sDir  = -1; break;
    }
    memset(visited,-1,sizeof(visited));
 
    cout << bfs() << endl;
}