题目描述
机器人移动学会(RMI)现在正尝试用机器人搬运物品。机器人的形状是一个直径 1.6 米的球。在试验阶段,机器人被用于在一个储藏室中搬运货物。储藏室是一个 N×M 的网格,有些格子为不可移动的障碍。机器人的中心总是在格点上,当然,机器人必须在最短的时间内把物品搬运到指定的地方。机器人接受的指令有:向前移动 1步(Creep);向前移动2步(Walk);向前移动 3步(Run);向左转(Left);向右转(Right)。每个指令所需要的时间为 1秒。请你计算一下机器人完成任务所需的最少时间。
输入格式:
第一行为两个正整数 N,M(N,M≤50)) ,下面 N 行是储藏室的构造, 0 表示无障碍, 1表示有障碍,数字之间用一个空格隔开。接着一行有 4 个整数和 1 个大写字母,分别为起始点和目标点左上角网格的行与列,起始时的面对方向(东 E,南 S ,西 W,北 N ),数与数,数与字母之间均用一个空格隔开。终点的面向方向是任意的。
输出格式:
一个整数,表示机器人完成任务所需的最少时间。如果无法到达,输出 −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
输出样例:
12
思路:
本题是搜索求最优解的问题,优先考虑BFS。现在假象你就是这个机器人,站在n行m列的矩阵里,可以选择行走或者转换方向的操作,每个操作都需要耗时1秒钟,其中行走一次可以走一步,两步,三步,转换方向可以上下左右。
既然本题求的是时间,那么影响时间的因素就是要么行走,要么转换方向。根据BFS的队列性质,每次需要将本次搜索得到的信息封装成一个结构体入队方便下次搜索。那么结构体内就得包含本次搜索到达的坐标、当前的方向、已经用掉的时间。
宽搜过程分两步讨论,第一步是当前不走路,只在原地转圈圈改变方向,因为调整方向也是需要耗时的。第二步就是按照当前方向,坚定行走,先尝试走一步,再尝试走两步,再尝试走三步,如果上述有一次的尝试碰到了障碍物,则后续的尝试都停止,因为如果走一步就碰到了障碍物,就不可能走两步,(不能飞过去,也不能穿墙术)。
本题有个难点就是如何保证这个方向不会绕圈圈走成一个回路,这就是稍微难一点的BFS题的特征,在标记数组vis上做文章,本题的vis需要三维数组,vis[i][j][k]=1表示按照k这个方向已经走到(i,j)过这个点。
本题还有个难点就是这个n*m的地图输入进来后,机器人是一坨球,一下占4个格子,解决办法就是在输入进来障碍物的坐标后,以这个点坐标作为一个小格子的右上角顶点,把这个小格子的四个顶点都标记上是障碍物。在宽搜中如果碰到障碍物就立即停止行走。
真正在代码实操环节,会被这道题的方向以及路径绕晕,向上走横坐标减少,向下走横坐标增加,向左走纵坐标增加,向右走纵坐标减少,跟直角坐标系的那一套彻底不一样。所以养成画图的好习惯特别重要,考场上的演算纸不是给你拿来叠纸飞机的,吼吼~
AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,a[55][55],vis[55][55][5],qx,qy,px,py,cha,ans=-1;
char ch;
//步数数组 一次走1、2、3步
int step[]={1,2,3};
//方向数组 向左转或向右转
int dir[]={-1,1};
struct Node
{
int x,y,time,dirt;
};
queue<Node> Q;
void BFS(int x,int y,int ch1)
{
Q.push((Node){x,y,0,ch1});
vis[x][y][ch1]=1;
while(!Q.empty())
{
Node cur=Q.front();
Q.pop();
if(cur.x==px&&cur.y==py)
{
ans=cur.time;
return;
}
//枚举方向数组
for(int j=0;j<2;j++)
{
int ndir=cur.dirt+dir[j];
//如果做减法变成0
if(ndir==0)ndir=4;
//如果做加法变成5
else if(ndir==5)ndir=1;
if(vis[cur.x][cur.y][ndir]==0)
{
Q.push((Node){cur.x,cur.y,cur.time+1,ndir});
vis[cur.x][cur.y][ndir]=1;
}
}
//枚举步数数组
for(int i=0;i<3;i++)
{
int cx,cy;
if(cur.dirt==1)
{
//向下(向南)走横坐标增加
cx=cur.x-step[i];
cy=cur.y;
}
else if(cur.dirt==2)
{
//向右(向东)走纵坐标增加
cx=cur.x;
cy=cur.y+step[i];
}
else if(cur.dirt==3)
{
//向下(向南)走横坐标增加
cx=cur.x+step[i];
cy=cur.y;
}
else if(cur.dirt==4)
{
//向左(向西)走纵坐标减少
cx=cur.x;
cy=cur.y-step[i];
}
//如果已经跑出地图或者这个球状机器人碰到了障碍物
if(n<=cx || cx<=0 || m<=cy || cy<=0 || a[cx][cy]==1){
break;
}
//如果这个点这个方向之前没来过
if(vis[cx][cy][cur.dirt]==0)
{
Q.push((Node){cx,cy,cur.time+1,cur.dirt});
vis[cx][cy][cur.dirt]=1;
}
}
}
}
int main()
{
bool non;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>non;
if(non)
{
a[i][j]=1;
a[i][j-1]=1;
a[i-1][j]=1;
a[i-1][j-1]=1;
}
}
}
//边界设置障碍物
for(int i=1;i<=n;i++)
{
a[n][i]=1;
a[i][m]=1;
}
cin>>qx>>qy>>px>>py>>ch;
if(ch=='N')cha=1;
else if(ch=='E')cha=2;
else if(ch=='S')cha=3;
else if(ch=='W')cha=4;
BFS(qx,qy,cha);
cout<<ans;
return 0;
}