机器人搬重物(BFS)

原题:

机器人搬重物

时间限制: 1 Sec  内存限制: 256 MB

题目描述

机器人移动学会(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,2,3步或者左右转向,耗时都为一秒,求最短耗时。
 
先贴代码(我知道你们是来看这个的)
自认为码风很干净的代码:
  1 #include<stdio.h>
  2 #include<iostream>
  3 #include<queue>
  4 using namespace std;
  5 struct node{ int x,y,s,z; }now;
  6 int n,m,sx,sy,ex,ey,ans,state,g[55][55],mark[55][55][5];
  7 //sx,sy起点坐标(start) ,ex,ey终点坐标(end) 
  8 //state标记最开始方向,g储存地图,mark标记
  9 //mark的三个下标分别确定行,列,方向 
 10 int d[4]={1,-1,2,-2},d2[3]={1,2,3};
 11 //d用于转换方向,d2用于移动 
 12 char ch;
 13 queue<node> q;
 14 bool check(int x,int y)
 15 {
 16     if(x<1||x>=n||y<1||y>=m)  return 0;
 17     //判断是否出界 
 18     if(g[x][y]||g[x][y+1]||g[x+1][y]||g[x+1][y+1])  return 0;
 19     //判断所在地是否有障碍物 
 20 return 1;
 21 }
 22 void bfs(int x,int y,int state)
 23 {
 24     for(int i=1;i<=n;i++)
 25         for(int j=1;j<=m;j++)
 26             for(int k=0;k<5;k++)  mark[i][j][k]=0x7fffffff;
 27     //初始化为最大值,方便下面更新最小值 
 28     mark[x][y][state+2]=0,q.push({x,y,0,state});
 29     while(!q.empty())
 30     {
 31         now=q.front();  q.pop();//取出队首 
 32         int x=now.x,y=now.y,s=now.s,z=now.z;
 33         for(int i=0;i<4;i++)
 34         {//依次判断四个方向移动是否可行 
 35             int tz,flag=0;
 36             if(d[i]==z)  tz=z,flag=1;
 37             else  if(d[i]+z)  tz=d[i];
 38             else  tz=d[i],flag=-1;
 39             //flag判断转换方向的特殊情况,1表示不动(0s),-1表示向后转(2s) 
 40             for(int j=1;j<=3;j++)
 41             {
 42                 int k,tx=x,ty=y,ts=s+2;//一般转向+移动共需2s 
 43                 if(flag==1)  ts--;//不转向共需1s 
 44                 else  if(flag==-1)  ts++;//不转向共需2s 
 45                 if(tz==2)//
 46                 {
 47                     for(k=1;k<=j;k++)
 48                     {
 49                         ty++;
 50                         if(!check(tx,ty))  break;
 51                         //移动过程中每一格都需要判断 
 52                     }
 53                     if(k<=j)  continue;//如果中途退出过表示不能移动 
 54                 }//以下代码复制略作修改即可 
 55                 else  if(tz==-2)//西 
 56                 {
 57                     for(k=1;k<=j;k++)
 58                     {
 59                         ty--;
 60                         if(!check(tx,ty))  break;
 61                     }
 62                     if(k<=j)  continue;
 63                 }
 64                 else  if(tz==1)//
 65                 {
 66                     for(k=1;k<=j;k++)
 67                     {
 68                         tx++;
 69                         if(!check(tx,ty))  break;
 70                     }
 71                     if(k<=j)  continue;
 72                 }
 73                 else  if(tz==-1)//
 74                 {
 75                     for(k=1;k<=j;k++)
 76                     {
 77                         tx--;
 78                         if(!check(tx,ty))  break;
 79                     }
 80                     if(k<=j)  continue;
 81                 }
 82                 if(mark[tx][ty][tz+2]>ts)
 83                 {
 84                     q.push({tx,ty,ts,tz});
 85                     mark[tx][ty][tz+2]=ts;
 86                 }
 87             }
 88         }
 89     }
 90 return;
 91 }
 92 int main()
 93 {
 94     scanf("%d %d",&n,&m);
 95     for(int i=1;i<=n;i++)
 96         for(int j=1;j<=m;j++)  scanf("%d",&g[i][j]);
 97     scanf("%d %d %d %d %c",&sx,&sy,&ex,&ey,&ch);
 98     switch(ch)
 99     {
100         case 'S':state=1;break;
101         case 'E':state=2;break;
102         case 'N':state=-1;break;
103         case 'W':state=-2;break;
104     }//东西南北四个方向 
105     bfs(sx,sy,state);
106     int A=min(mark[ex][ey][0],mark[ex][ey][1]);
107     int B=min(mark[ex][ey][3],mark[ex][ey][4]);
108     ans=min(A,B);//寻找在终点时的最小步数 
109     if(ans!=0x7fffffff)  printf("%d
",ans);
110     else  printf("-1
");//若没有被更新过代表无解 
111 return 0;
112 }

分块解析:

       BFS基本思路:把所有可行的、未标记过的点依次入队(当然了首先入队的是起点),依次取出后按所有可能性拓展,若没有可以再拓展的点代表搜索完毕。

       注意点1:一个机器人占据四个格子,所以代码中都使用左上角的坐标来储存。

       注意点2:题目中描述的允许操作是

①向前移动 1 步(Creep

②向前移动 2 步(Walk

③向前移动 3 步(Run

向左转(Left

向右转(Right

       在这里,允许的操作是左右旋转,所以我们需要宏观的东南西北来判断机器人的旋转方向。

       不妨使用2,-2,1,-1分别代表东、西、南、北,只要依次让机器人尝试这些方向,如果当前尝试的方向与原来的朝向的和为0,代表向后转;同样的如果相同即没有旋转,其他情况都为向左或右旋转。

       旋转了就必须移动(这不是废话嘛......不然你转他干什么),所以可以将旋转与移动合并为一次操作计时。

       注意在mark数组记录的时候要记录2个数据:坐标以及朝向。只有在同一地点、同一种朝向的情况下,如果当前方案比之前mark标记的最优方案更优才能更新。

       注意点3:判断目前搜索到的点是否可行,代码如下

bool check(int x,int y)
{
    if(x<1||x>=n||y<1||y>=m)  return 0;
    if(g[x][y]||g[x][y+1]||g[x+1][y]||g[x+1][y+1])  return 0;
return 1;
} 

       要注意一个点!机器人不管是爬还是走还是跑,过去的途中也不可以碰到障碍物!

       例如样例中的这个图,机器人(左上角)从网格第四行第一列直接向北跑三格到第一行第一列,若只判断左上角的目标地点是否可行的话,是可以移动的,但实际上这条路线是不被允许的,因为移动过程中会被障碍物挡住(毕竟机器人不是飞过去的),我就是因为这个WA了三次QAQ。所以我的做法是移动过程中碰到的的点依次判断是否可行。具体实现过程见代码第45行至81行。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/leaf-2234/p/13861249.html