题目连接:该题是luogu试炼场的2-8:T4
题目大意:
1 给出一个0,1棋盘,0表示空格,1表示有柱子。
2 给出机器人的起始坐标和起始方向,给出结束位置,求机器人最少需要多少步数到达;
3 要求1:机器人本身有体积;
4 要求2:机器人消耗步数有以下几种方式:
1)前进1格;
2)前进2格;
3)前进3格;
4)原地左转(方向改了,位置没变);
5)原地右转(方向改了,位置没变);
思路分析:迷宫的宽搜的拓展问题:
1 要解决体积问题:如下图分析:地图转换
1.1 黑色坐标系和格子中的灰色,表示题目要求的原始地图;
1.2 蓝色坐标系和交叉点,是机器人中心点实际可以落脚的地方;
1.3 因为机器人有体积,所以红色的交点,都不能到达;
1.4 黄色的线条,是机器人真实的路径。
2 机器人移动的方向问题:
解题步骤:
1 完成地图转换:
1.1 因为机器人有体积:地图要从格子转换为线的交点;
1.2 机器人的中心点,只能落在上图中蓝色区域的非红色交点上;
1.3 预处理的时候解决两个问题:
1)封边:机器人有体积,所以不能走边缘;
2)封路:障碍格子的四个角,机器人都不能走;
2 动的方法:
2.1 移动的过程除了要记录位置,还要记录方向;
2.2 移动分两种情况:
1)向前移动:1,2,3步;
2)原地转向:
右转:步数+1;
左转:步数+1;
向后转:步数+2;
上代码(详细解说):
//luogu1126:机器人搬重物
//宽搜+地图转换+方向组合
//地图转换:
//1 地图要从格子转换为线的交点:因为机器人有体积;
//2 机器人的中心点,只能落在蓝色区域的非红色交点上;
//3 预处理的时候解决两个问题:
// 1)封边:机器人有体积,所以不能走边缘;
// 2)封路:障碍格子的四个角,机器人都不能走;
//移动的方法:
//1 移动的过程除了要记录位置,还要记录方向;
//2 移动分两种情况:
// 1)向前移动:1,2,3步;
// 2)原地转向:
// 右转:步数+1;
// 左转:步数+1;
// 向后转:步数+2;
//剩下就是宽搜的熟练程度了~
#include<bits/stdc++.h>
using namespace std;
int n,m,sx,sy,ex,ey;
char st;
int a[110][110];//线的交汇点:"机器人中心点"能走的地方
void inp()//输入与地图转换
{
scanf("%d %d",&n,&m);//格子地图
memset(a,0,sizeof(a));//0可以走,-1不能走
//封边:因为机器人有体积,不能走最外面的红色框线
for(int i=0;i<=n;i++)
{
a[i][0]=-1;//左边缘不能走
a[i][m]=-1;//右边缘不能走
}
for(int i=0;i<=m;i++)
{
a[0][i]=-1;//上边缘不能走
a[n][i]=-1;//下边缘不能走
}
//封路:将有障碍的格子的四个角(交点)封住
for(int i=1;i<=n;i++)//i,j表示格子
{
for(int j=1;j<=m;j++)
{
int x;scanf("%d",&x);//原始格子
if(x>0)//封四角
{
a[i-1][j-1]=-1;//左上角
a[i-1][j] =-1;//右上角
a[i][j-1] =-1;//左下角
a[i][j] =-1;//右下角
}
}
}
//输入(sx,sy),(ex,ey), 初始时st方向
scanf("%d %d %d %d %c",&sx,&sy,&ex,&ey,&st);
}
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
struct nod{int x,y,t,s;}f[10010];
int tou,wei;
bool b[110][110][5];//对于(x,y)点,t方向是否来过
void bfs()//宽搜的过程
{
//方向预处理方向:
int tt;//初始方向
if(st=='E') tt=0; if(st=='S') tt=1;
if(st=='W') tt=2; if(st=='N') tt=3;
//队列初始化
tou=1;wei=2;
f[tou].x=sx; f[tou].y=sy; //坐标
f[tou].t=tt; f[tou].s=0; // 方向与步数
memset(b,0,sizeof(b));
b[sx][sy][tt]=1;//封路
//宽搜
while(tou<wei)
{
int x=f[tou].x;
int y=f[tou].y;
int t=f[tou].t;
//到了终点
if(x==ex&&y==ey) { printf("%d",f[tou].s); exit(0); }
//当前方向向前冲:1-3步
for(int i=1;i<=3;i++)
{
//向前移动 i 步
int nx=x+dx[t]*i;
int ny=y+dy[t]*i;
//边界判断与障碍
if(nx<1||nx>=n||ny<1||ny>=m||a[nx][ny]==-1) break;
//可落脚并且,该方向没来过
if(a[nx][ny]==0&&b[nx][ny][t]==0)
{
b[nx][ny][t]=1;//封路
f[wei].x=nx;
f[wei].y=ny;
f[wei].t=t;
f[wei].s=f[tou].s+1;
wei++;
}
}
//原地转向
for(int i=1;i<=3;i++)//可以转3个方向
{
t++;if(t>3) t=0;
if(b[x][y][t]==0)//当前点:未试过以这个方向到达
{
b[x][y][t]=1;
f[wei].x=x;
f[wei].y=y;
f[wei].t=t;
//初始方向和结束方向比较,确定转了几次
if(abs(t-f[tou].t)==2) f[wei].s=f[tou].s+2;
else f[wei].s=f[tou].s+1;
wei++;
}
}
tou++;
}
}
int main()
{
inp();//输入与地图转换
//起点或者终点在边框
if(sx>=n||sx<1||sy>=m||sy<1)
{
printf("-1");return 0;
}
//起点或者终点不能落脚
if(a[sx][sy]==-1||a[ex][ey]==-1)
{
printf("-1");return 0;
}
bfs();//经典宽搜
printf("-1");//无解
return 0;
}