题目链接

题目描述

……机器人的形状是一个直径1.61.6米的球……储藏室是一个N×MN×M的网格,有些格子为不可移动的障碍。机器人的中心总是在格点上……

输入格式

……分别为起始点和目标点左上角网格的行与列……终点的面向方向是任意的。

参考代码

const int MAXN = 100;

struct node
{
	int x,y,asp,Step;
	node(void) : x(0),y(0),asp(0),Step(0) {}
	node(int _x,int _y,int _asp,int _Step) : x(_x),y(_y),asp(_asp),Step(_Step) {}
}S,T,cur;

char asp;
int n,m,ans = INT_MAX;
int base[MAXN][MAXN],Map[MAXN][MAXN],vis[MAXN][MAXN][5];
int turn[5][4] = {{},{0,4,2},{0,1,3},{0,2,4},{0,3,1}};
queue<node> Q;

void bfs()
{
	Q.push(S);
	if (Map[S.x][S.y] == 1) return;//特判:起点处有障碍的情况。
	while (!Q.empty())
	{
		cur = Q.front();
		Q.pop();
		if (cur.x == T.x && cur.y == T.y)
		{
			ans = min(ans,cur.Step);
			continue;
		}//判断是否到终点。
		for (int i = 1;i <= 5;++i)
		{
			int nx = cur.x;
			int ny = cur.y;
			int na = cur.asp;
			if (i <= 3)
			{
				if (na == 1) nx -= i;
				else if (na == 2) ny += i;
				else if (na == 3) nx += i;
				else ny -= i;
			}
			else na = turn[na][i - 3];
			if (nx < 1 || ny < 1 || nx > n - 1 || ny > m - 1 || vis[nx][ny][na]) continue;
			if (Map[nx][ny] == 1 && (i >= 1 && i <= 3))
			{
				i = 3;
				continue;
			}
			Q.push(node(nx,ny,na,cur.Step + 1));
			vis[nx][ny][na] = true;//访问标记。
		}
	}
}

void init()
{
	scanf("%lld%lld",&n,&m);
	for (int i = 0;i < n;++i)
		for (int j = 0;j < m;++j)
			scanf("%lld",&base[i][j]);
	for (int i = 0;i < n;++i)
		for (int j = 0;j < m;++j)
			if (base[i][j]) Map[i][j] = Map[i + 1][j] = Map[i][j + 1] = Map[i + 1][j + 1] = 1;
	scanf("%lld%lld%lld%lld %c",&S.x,&S.y,&T.x,&T.y,&asp);
	if (asp == 'N') S.asp = 1;
	else if (asp == 'E') S.asp = 2;
	else if (asp == 'S') S.asp = 3;
	else S.asp = 4;
}

void work()
{
	bfs();
	if (ans == INT_MAX) printf("-1");
	else printf("%lld",ans);
}

分析

  • BFS细节题。

解题报告:洛谷 P1126 机器人搬重物
(图片来源:https://www.luogu.org/blog/lrt/solution-p1126)

本题有题干中有一些注意点,在上面已经标注。那么,对于测试数据,按照我们一般的思维,地图应该就是像上图那样,蓝框内的绿色点可以走,橙色点不能。

为此,在读入数据的时候,先用base[][]数组保存每个障碍的左上角顶点的坐标,然后再实际的情况写入Map[][]数组:

for (int i = 0;i < n;++i)
	for (int j = 0;j < m;++j)
		scanf("%lld",&base[i][j]);
for (int i = 0;i < n;++i)
	for (int j = 0;j < m;++j)
		if (base[i][j]) Map[i][j] = Map[i + 1][j] = Map[i][j + 1] = Map[i + 1][j + 1] = 1;

另外需要注意的是,机器人只能向左转或是向右转。作者一开始以为机器人是可以一下子转到背后的qwqqwq

设北、东、南、西分别为1、2、3、4,对于每个朝向,预处理它左右转可以面朝哪个方向:

int turn[5][4] = {{},{0,4,2},{0,1,3},{0,2,4},{0,3,1}};

然后就可以BFS了。作者在写BFS时,遇到了一个问题:假如现在机器人的坐标是(2,5)(2,5)(绿色),方向朝北,如果走一步,显然会碰到障碍,但是如果直接枚举走的步数的话,好像又可以“飞”过去,这显然不对了。怎么办呢?

显然,如果走一步都会碰到障碍,那么走2步、3步都是不行的,因此我们可以直接开始枚举转向:

if (Map[nx][ny] == 1 && (i >= 1 && i <= 3))
{
	i = 3;
	continue;
}

最后为了避免重复扩展,对于每个坐标的每个方向,都使用一个数组记录下来,这样若后面再次枚举到这个状态,直接跳过即可:

vis[nx][ny][na] = true;

若有谬误,敬请斧正。

相关文章:

  • 2021-07-17
  • 2021-06-30
  • 2021-05-17
  • 2021-12-13
  • 2022-01-18
  • 2021-06-09
  • 2021-08-22
  • 2021-09-22
猜你喜欢
  • 2021-06-23
  • 2021-05-01
  • 2021-11-19
  • 2021-11-15
  • 2022-12-23
相关资源
相似解决方案