洛谷 P1126 机器人搬重物
机器人搬重物
咕咕咕了几个星期,终于静下心来写完了这篇题解
这道题的坑点还是比较多的,下面会一一列举。
先审题:
根据题目的意思,这是一道走迷宫的问题,显然用bfs去求解。
首先,我们需要建一张图。。。
这里很多人下意识地将题目中输入的图存下,这就涉及到了此题的第一个坑点了:
坑点1:应建立n+1 * m+1 大小的图
不理解?不讲道理?
这里我们要重新回到题目中,题目的描述为:机器人的形状是一个直径$1.6米的球,而机器人走的是格点,而障碍物存在于格子上,这就意味着每一个有障碍物存在的格子的四个端点都是不能够通行的,而我们操作的是机器人,所以对于一张n*m的格子图自然需要建一张(n+1) * (m+1)的格点图 。
建图:
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) {
scanf("%d", &a);
if(a == 1) {
vis[i][j] = vis[i][j+1] = vis[i+1][j] = vis[i+1][j+1] = 1;
map[i][j] = map[i][j+1] = map[i+1][j] = map[i+1][j+1] = 1;
}
}
n++, m++;
这里的vis数组用于后面bfs中判断是否到过,而当该点本身无法到达时,直接置为1。
而这里的map数组只是单纯用于存这个点周围是否有障碍物,具体用处后面会提到。
如果你以为这就结束了,那么就大错特错了,还要再加两行:
for(int i = 1; i <= m; i++) vis[1][i] = vis[n][i] = 1;
for(int i = 1; i <= n; i++) vis[i][1] = vis[i][m] = 1;
What? Why?
仔细想想,以机器人的体积,它是无法走到第一行(第一列)的格点以及最后一行(最后一列)的格点上去的(旁边是墙!!!)
坑点2:四周的边界走不了
再审题
题中增加了方向的问题,以及机器人每次能走1~3步。
对于方向,我们可以考虑在bfs用到的结构体中增加方向的值,即使用dirt来表示该时刻机器人的方向。
结构体
struct node{
int x, y, step, dirt, turn;//dirt取值为1, 2, 3, 4时别表示东,西,南,北。
node (int xx, int yy, int ss, int dd, int tt) : x(xx), y(yy), step(ss), dirt(dd), turn(tt){}
};
接下来就是主要的bfs函数了。
inline void bfs(int x1, int y1, int x2, int y2, char to)
{
int drt;
if(to == 'E') drt = 1;
else if(to == 'W') drt = 2;
else if(to == 'S') drt = 3;
else drt = 4;
q.push(node(x1, y1, 0, drt, 0));
while(!q.empty()) {
node top = q.front();
q.pop();
int dx, dy;
、、、
}
以上部分与正常的bfs打法无异,只是单纯的多了一个预处理方向的过程,to就是最初的方向。
接着我们考虑用for循环来实现行走与转向的问题。
1.正常的行走(i = 1、2、3时)
for(int i = 1; i <= 5; i++) {//i=4表示左转, i=5表示右转
if(i != 4 && i != 5) {
drt = top.dirt;
if(drt == 1) {
dx = top.x;
dy = top.y + i;
}
else if(drt == 2) {
dx = top.x;
dy = top.y - i;
}
else if(drt == 3) {
dx = top.x + i;
dy = top.y;
}
else {
dx = top.x - i;
dy = top.y;
}
if(dx <= 0 || dy <= 0 || dx > n || dy > m) {
i = 3;//如果走了该步数已经超出了边界,那么该步数以后的步数必然会超过边界,这样直接跳到转向部分即可。
continue;
}
if(vis[dx][dy]) {
continue;
}
if(dx == x2 && dy == y2) {
dis[x2][y2] = top.step + 1; //到达终点,存值,返回。
return;
}
dis[dx][dy] = min(top.step + 1, dis[dx][dy]);
vis[dx][dy] = 1;
q.push(node(dx, dy, top.step+1, drt, 0));
}
这里引入的dis数组存的是到达某点的最短时间。
2.转向(i = 4、5时)
else if(i == 4){
dx = top.x, dy = top.y;
drt = top.dirt;
if(drt == 1) drt = 4;
else if(drt == 2) drt = 3;
else if(drt == 3) drt = 1;
else drt = 2;
dis[dx][dy] = min(top.step + 1, dis[dx][dy]);
vis[dx][dy] = 1;
q.push(node(dx, dy, top.step + 1, drt, t+1));
}
else {
dx = top.x, dy = top.y;
drt = top.dirt;
if(drt == 1) drt = 3;
else if(drt == 2) drt = 4;
else if(drt == 3) drt = 2;
else drt = 1;
dis[dx][dy] = min(top.step + 1, dis[dx][dy]);
vis[dx][dy] = 1;
q.push(node(dx, dy, top.step + 1, drt, t+1));
}
}
这里左转右转时的方向要注意,别把自己弄晕了!
然后写好主函数,我们就可以愉快地 ac了
!诶,WA?为什么??
输入坐标的时候加上这个试试
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
getchar();
x1++, x2++, y1++, y2++;
scanf("%c", &to);
这样做的原因看完样例相信聪明的你就明白了。
然后,再提交?怎么又错了几个点啊!!
坑点3:终点可能在无法到达的格点上
坑点4: 起点和终点可能是同一个点
对于上述的两个坑点,只需要两个if特判就可以了
if(vis[x1][y1] || vis[x2][y2]) return 0 * printf("-1\n");
if(x1 == x2 && y1 == y2) return 0 * printf("0\n");
这样做了之后好像还是ac不了诶。
这里我们回头看看bfs里的for循环,其中有两个问题我们需要解决一下:
q1:对于同一个点,for循环中的i = 4、5可能会使机器人一直在该点转向。
q2:有没有想过这样一个问题,如果机器人在往前走一步的时候遇到了障碍物,那么它前面的两个点vis都为1,但当它往前走三步时,该个点的vis = 0,即机器人直接穿过了障碍物到达了障碍物后面的点,这显然是不符合实际的。
对于q1,我们就需要用到node结构体里定义的turn作为一个转向次数计数器去判断,当机器人在某个点转向3次即以上时,直接跳过。
对于q2,我们就需要用到之前定义的map数组去判断了。
考虑完了上述的所有坑点后,这道题终于成功的AC了(什么?起始转向次数有设置坑点?不管了,反正我没有遇到)
code:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
//Mystery_Sky
//
#define INF 0x3f3f3f3f
const int M = 100;
struct node{
int x, y, step, dirt, turn;//dirt取值为1, 2, 3, 4时别表示东,西,南,北。
node (int xx, int yy, int ss, int dd, int tt) : x(xx), y(yy), step(ss), dirt(dd), turn(tt){}
};
queue <node> q;
int n, m, ans, x1, x2, y1, y2;
char to;
int dis[M][M], map[M][M];
bool vis[M][M];
inline void bfs(int x1, int y1, int x2, int y2, char to)
{
int drt;
if(to == 'E') drt = 1;
else if(to == 'W') drt = 2;
else if(to == 'S') drt = 3;
else drt = 4;
q.push(node(x1, y1, 0, drt, 0));
while(!q.empty()) {
node top = q.front();
q.pop();
int dx, dy;
for(int i = 1; i <= 5; i++) {//i=4表示左转, i=5表示右转
if(i != 4 && i != 5) {
drt = top.dirt;
if(drt == 1) {
dx = top.x;
dy = top.y + i;
}
else if(drt == 2) {
dx = top.x;
dy = top.y - i;
}
else if(drt == 3) {
dx = top.x + i;
dy = top.y;
}
else {
dx = top.x - i;
dy = top.y;
}
if(map[dx][dy] == 1) i = 3;
if(dx <= 0 || dy <= 0 || dx > n || dy > m) {
i = 3;
continue;
}
if(vis[dx][dy]) {
continue;
}
if(dx == x2 && dy == y2) {
dis[x2][y2] = top.step + 1;
return;
}
dis[dx][dy] = min(top.step + 1, dis[dx][dy]);
vis[dx][dy] = 1;
q.push(node(dx, dy, top.step+1, drt, 0));
}
else if(i == 4){
int t = top.turn;
if(t > 2) continue;
dx = top.x, dy = top.y;
drt = top.dirt;
if(drt == 1) drt = 4;
else if(drt == 2) drt = 3;
else if(drt == 3) drt = 1;
else drt = 2;
dis[dx][dy] = min(top.step + 1, dis[dx][dy]);
vis[dx][dy] = 1;
q.push(node(dx, dy, top.step + 1, drt, t+1));
}
else {
int t = top.turn;
if(t > 2) continue;
dx = top.x, dy = top.y;
drt = top.dirt;
if(drt == 1) drt = 3;
else if(drt == 2) drt = 4;
else if(drt == 3) drt = 2;
else drt = 1;
dis[dx][dy] = min(top.step + 1, dis[dx][dy]);
vis[dx][dy] = 1;
q.push(node(dx, dy, top.step + 1, drt, t+1));
}
}
}
return;
}
int main() {
int a;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) {
scanf("%d", &a);
if(a == 1) {
vis[i][j] = vis[i][j+1] = vis[i+1][j] = vis[i+1][j+1] = 1;
map[i][j] = map[i][j+1] = map[i+1][j] = map[i+1][j+1] = 1;
}
}
n++, m++;
for(int i = 1; i <= m; i++) vis[1][i] = vis[n][i] = 1;
for(int i = 1; i <= n; i++) vis[i][1] = vis[i][m] = 1;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
getchar();
x1++, x2++, y1++, y2++;
scanf("%c", &to);
memset(dis, INF, sizeof(dis));
if(vis[x1][y1] || vis[x2][y2]) return 0 * printf("-1\n");
if(x1 == x2 && y1 == y2) return 0 * printf("0\n");
bfs(x1, y1, x2, y2, to);
if(dis[x2][y2] == INF) printf("-1\n");
else printf("%d\n", dis[x2][y2]);
return 0;
}