【题解】洛谷P1126 机器人搬重物(尚贤) 详细注释

洛谷P1126 机器人搬重物  详细注释


题目传送门


这道题,毫无疑问,就是广搜。但是它要注意的细节非常多。所以这道题对逻辑和代码能力的的考察很高

首先,这道题需要注意,障碍物是在格子上,而机器人是在格点上走。某人就是我自信地写完了代码后发现题读错了。关键最讨厌的是样例居然能过......

那么这道题经过信息整理可以发现,我们需要两个数组:一个存方格上障碍物的位置(map[i][j]数组表示),一个存机器人可以走的格点的位置(map_[i][j])数组表示,则根据样例我们可以整理成这张图: 

【题解】洛谷P1126 机器人搬重物(尚贤) 详细注释_第1张图片

其中黑色为数组map[i][j]的下标,绿色为map_[i][j]的下标。根据题目可以发现,机器人本身也有宽度,所以边界和障碍物的四周,不能走,那么机器人可以到达的地方就是图中蓝色框内的绿色格点。


代码如下:

// 题目:https://www.luogu.com.cn/problem/P1126 
// (东E,南S,西W,北N) 
//     上=0 
// 左=2    右=3 
//     下=1
// 注:上 = 北,下 = 南,左 = 西,右 = 东 
#include 
#include 
#include 
using namespace std;
const int size = 50 + 5;
bool map[size][size], map_[size][size];
// map 代表的是当前的格子有没有障碍,注意,这是格子,不是线段 
// map2 代表的是当前的两点交叉的线段的点可不可走,注意,机器人是在线段上走,而不是在格子上走 
// 这里的线段是有(n + 1) * (m + 1)条,所以我的数组下标是从0~n,0~m,注意,我的map数组下标是从1开始的 
int number1[4][4] = {{0, 2, 1, 1}, {2, 0, 1, 1}, {1, 1, 0, 2}, {1, 1, 2, 0}}; // number1[i][j] 表示从方向i到方向j所需的步数,当然,在我这个程序里是没有用到的 
int number2[2][4] = {{2, 3, 1, 0}, {3, 2, 0, 1}}; // number2[i][j] i = 0时表示是左转,i = 1时表示是右转,j表示当前方向向方向i转动一步后的方向 
int number3[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // number3[i][j] 表示向当前方向前进1步的xy坐标的变化,j = 0代表这是x坐标的变化,j = 1代表这是y坐标的变化 
bool vis[size][size][5]; //vis[i][j][k] 代表当前的两个状态有没有被经历过,ij代表的是当前的坐标,k代表当前的方向 
struct node{
	int x, y, z, step;
	// xy分别代表当前的位置
	// z代表当前的方向,详细见第2、3、4行代码 
	// step代表当前所走的步数 
	node() {
	}
	node(const int &x_, const int &y_, const int &z_, const int &step_) {
		x = x_; y = y_; z = z_; step = step_; 
	}
	// 构造函数 
	void print() { // 这是调试用的 
		printf("%d %d %d %d\n", x, y, z, step);
	}
};

/*
参数作用:
	1.ch	代表当前方向的所属的字母,详见第1行代码
函数作用:
	将当前的方向的所属的字母转化为数字,详细见第2、3、4行代码 
*/ 
int count(const char &ch) {
	if (ch == 'N') return 0;
	if (ch == 'S') return 1;
	if (ch == 'W') return 2;
	return 3;
	// 详细见第2、3、4行代码 
}

int main() {
	freopen("cpp.in", "r", stdin);
	freopen("cpp.out", "w", stdout);
	int n, m, sx, sy, ex, ey;
	char ch;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			cin >> map[i][j]; 
	scanf("%d%d%d%d %c", &sx, &sy, &ex, &ey, &ch); // 空格要打 
	// 以上是读入部分 
	// ++sx, ++sy, ++ex, ++ey; 这里是我之前写错的地方,其实是不用加1,因为我们的map_的范围是从0~n, 0~m的 
	// 这里是将坐标从格子变为线段 
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) 
			if (map[i][j]) // 如果当前格子上有障碍物,那么我们要将map_的相关位置也赋值为不可走 
				map_[i][j] = map_[i][j - 1] =  map_[i - 1][j] = map_[i - 1][j - 1] = 1; // 这条式子是用来标记map_中不可走的点,是通过观察得来的,具体的可以自己观察一下 
				// map_[i][j] = map_[i][j + 1] = map_[i + 1][j] = map_[i + 1][j + 1] = 1; // 上次的代码是写了这条式子,因为我之前吧数组下标看成了从1开始 
	}
	// 调试代码
	/* 
	for (int i = 0; i <= n; ++i) {
		for (int j = 0; j <= m; ++j) {
			cout << map_[i][j];
		}
		cout << endl;
	}
	*/
	// 调试代码 
	queue q;
	q.push(node(sx, sy, count(ch), 0));
	vis[sx][sy][count(ch)] = true; // 将初始状态加入队列并将初始状态赋值为走过
	while (!q.empty()) {
		node now = q.front();
		q.pop();
		// now.print(); // 调试代码 
		if (now.x == ex && now.y == ey) { // 如果到了终点,那直接可以输出步数,然后return; 
			printf("%d", now.step);
			return 0;
		}
		++now.step; // 这里的步数++是因为我们现在要执行1秒的命令 
		// 左转
		now.z = number2[0][now.z]; // 先转一下 
		if (!vis[now.x][now.y][now.z]) { // 如果当前状态没经历过 
			q.push(now); // 先加入队列 
			vis[now.x][now.y][now.z] = true; // 在将当前状态赋值为经历过 
		}
		now.z = number2[1][now.z]; // 然后转回来 
		// 右转
		now.z = number2[1][now.z]; // 先转一下
		if (!vis[now.x][now.y][now.z]) { // 如果当前状态没经历过 
			q.push(now); // 先加入队列 
			vis[now.x][now.y][now.z] = true; // 在将当前状态赋值为经历过 
		}
		now.z = number2[0][now.z]; // 然后转回来 
		// 前进
		// 这里我采用的策略是一步一步走,先走一步,加入queue。然后再走一步(现在一共走了2步),加入queue。然后再走一步(现在一共走了3步),加入queue。
		for (int i = 1; i <= 3; ++i) {
			now.x += number3[now.z][0], now.y += number3[now.z][1];
			if (now.x < 1 || now.y < 1 || now.x >= n || now.y >= m/*这个判断有没有出界的条件是经观察所发现的,具体的可以自己观察*/ || map_[now.x][now.y] /*之前写代码的时候忘记加这句话了,这里加这个条件是因为如果我们的机器人走到一半撞墙了,接下来如果再前进,也只会在走的途中撞墙*/) break; // 中途如果已经走出了储藏室或者撞墙了,那代表继续走下去只会更加超限,所以直接return; 
			if (vis[now.x][now.y][now.z]) continue; // 如果当前状态已经遍历过,那是要continue,注意,当前的状态走过不代表之后的状态走过 
			// 如果没有出界,没有撞墙,没有经历过 
			q.push(now); // 那就将当前状态加入队列 
			vis[now.x][now.y][now.z] = true; // 并将当前的状态赋值为经历过 
		} 
	}
	printf("-1");
	return 0;
} 

 

你可能感兴趣的:(【题解】洛谷P1126 机器人搬重物(尚贤) 详细注释)