P1126 机器人搬重物【普及+/提高】题解
(最近给新来的同学们讲了讲广搜,正好巩固一下,所以最近广搜的题解比较多)
这道题是广搜里面细节很多的一道。
首先,我们要预处理一下输入数据。输入数据只表现了哪些点有障碍物,由于
机器人的形状是一个直径1.6米的球。
所以障碍物上下左右相邻的格子都是无法到达的,同时机器人只能在格子边线上走,而输入数据是按照格子本身来输入的。我们可以使用另外一个二维bool
数组map[i][j]
表示((i,j))这个点能不能到达。其中(i,j)都是从(0)开始索引编号的。如果在输入数据中,((i,j))是(1),那么:
(1)map[i][j]=1
;
(2)map[i-1][j]=map[i][j-1]=map[i-1][j-1]=1
。
你们可以理解为输入数据的矩阵向左上方偏移了一个单位,得到map[i][j]
,再对四周的格子进行处理。请读者自行画图,感性理解。
bool vis[55][55][4];//一个状态(前两维是左边,后一维是面向方向)有没有被访问过
bool map[55][55];//一个点有没有障碍物
//vis和map也可以合起来写成一个数组,这里推荐使用两个。
void init(){
cin>>n>>m;
int tmp;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>map[i][j];
if(map[i][j]){
map[i-1][j]=map[i][j-1]=map[i-1][j-1]=1;
}
}
}
然后就是最后五个数据,分别用stx,sty,fnx,fny,stface
这五个变量存储起来。方向(最后一个数据)是以字符类型输入的,我们可以转换一下,便于之后的处理:
//(接上一块代码:init())
char ss;
cin>>stx>>sty>>fnx>>fny;
cin>>ss;
switch(ss){
case 'E':stface=E;break;case 'W':stface=W;break;
case 'S':stface=S;break;case 'N':stface=N;break;
}
}
其中E,W,S,N
已经在开头定义,注意一定要根据顺时针或逆时针的顺序排列:
const int E=0,S=1,W=2,N=3;
我们知道,搜索(无论是广搜还是深搜)中,一个很重要的环节就是“状态“。题目明显地告诉我们,机器人的坐标和面向方向可以组成一个状态。当然还有走到这一个状态花费的时间:
struct cond{
int x,y;
int face;
int dist;
};
然后就是广搜的基本函数了。这里一定要注意:
1.执行转向操作时,只要在队头状态的dist
基础上减1或加1就可以了(这里也体现出const int E=0,S=1,W=2,N=3;
中必须按方向顺时针或逆时针排列的必要性),但是如果dist +or- 1
得到的新的面向方向小于(0)或大于(3),就必须要转换到(0 o 3)之间的数值,具体表现为(-4 or +4)。
2.执行运动操作时,如果机器人向前走一步就遇到了障碍物,那么向前走一步是不可能的了。那向前走两步或是三步呢?很明显更不可能,要不然机器人就穿墙了。我第一次写代码就没有考虑到这点,误认为在向前走一步不合法的情况下,走两步和走三步仍然合法,导致了样例都没过。解决方法是,在扩展状态的循环中,按走一步到走三步的顺序尝试扩展,当发现有不合法状态时直接break
。
其他就都差不多了,详见代码:
bool ok(int x,int y){
return (x>0)&&(x<n)&&(y>0)&&(y<m);//是否在地图范围内
}
void bfs(){
queue<cond> q;
q.push((cond){stx,sty,stface,0});
vis[stx][sty][stface]=true;
while(!q.empty()){
cond now=q.front();
q.pop();
const int x=now.x,y=now.y,face=now.face,d=now.dist;
if(x==fnx&&y==fny){//目标状态
cout<<d<<endl;
return;
}
for(int i=1;i<=3;i++){
int nx=x+i*dx[face],ny=y+i*dy[face];
if(!ok(nx,ny)||map[nx][ny]) break;//注意点2
else if(!vis[nx][ny][face]){
vis[nx][ny][face]=true;
q.push((cond){nx,ny,face,d+1});
}
}
int nface1=face+1,nface2=face-1;
if(nface1>3) nface1-=4;//注意点1
if(nface2<0) nface2+=4;
if(ok(x,y)&&(!vis[x][y][nface1])){
vis[x][y][nface1]=true;
q.push((cond){x,y,nface1,d+1});
}
if(ok(x,y)&&(!vis[x][y][nface2])){
vis[x][y][nface2]=true;
q.push((cond){x,y,nface2,d+1});
}
}
cout<<-1<<endl;
}
AC代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int E=0,S=1,W=2,N=3;
bool vis[55][55][4];
bool map[55][55];
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
int n,m;
int stx,sty,fnx,fny,stface;
struct cond{
int x,y;
int face;
int dist;
};
void init(){
cin>>n>>m;
int tmp;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>map[i][j];
if(map[i][j]){
map[i-1][j]=map[i][j-1]=map[i-1][j-1]=1;
}
}
}
char ss;
cin>>stx>>sty>>fnx>>fny;
cin>>ss;
switch(ss){
case 'E':stface=E;break;case 'W':stface=W;break;
case 'S':stface=S;break;case 'N':stface=N;break;
}
}
bool ok(int x,int y){
return (x>0)&&(x<n)&&(y>0)&&(y<m);
}
void bfs(){
queue<cond> q;
q.push((cond){stx,sty,stface,0});
vis[stx][sty][stface]=true;
while(!q.empty()){
cond now=q.front();
q.pop();
const int x=now.x,y=now.y,face=now.face,d=now.dist;
if(x==fnx&&y==fny){
cout<<d<<endl;
return;
}
for(int i=1;i<=3;i++){
int nx=x+i*dx[face],ny=y+i*dy[face];
if(!ok(nx,ny)||map[nx][ny]) break;
else if(!vis[nx][ny][face]){
vis[nx][ny][face]=true;
q.push((cond){nx,ny,face,d+1});
}
}
int nface1=face+1,nface2=face-1;
if(nface1>3) nface1-=4;
if(nface2<0) nface2+=4;
if(ok(x,y)&&(!vis[x][y][nface1])){
vis[x][y][nface1]=true;
q.push((cond){x,y,nface1,d+1});
}
if(ok(x,y)&&(!vis[x][y][nface2])){
vis[x][y][nface2]=true;
q.push((cond){x,y,nface2,d+1});
}
}
cout<<-1<<endl;
}
int main()
{
ios::sync_with_stdio(false);
init();
bfs();
return 0;
}