这道题就是一道单纯的bfs,不过难点是如何表示转弯,也就是说当人面朝南的时候,左转,右转,前进分别对应着矩阵中的上下左右的哪几种情况呢?这道题和刘汝佳书上的一道迷宫类似,那道题的难点也是如何表示方向。
我们假设0,1,2,3分别表示北,西,南,东。
一般的bfs我们不都是要设一个行走数组吗, 现在我们把数组设成
const int dx[] = {0, 1, 0, -1};//左下右上
const int dy[] = {-1, 0, 1, 0};
我们bfs的状态用三个域表示,横坐标x,纵坐标y,面朝的方向dir。
我们给出公式,假设当前人的状态是x,y,dir
左转之后的状态就是 x + dx[dir], y + dy[dir],(dir + 1) % 4
右转之后的状态就是 x + dx[(dir+2)%4],y + dy[(dir+2)%4], (dir + 3) % 4
前进之后的状态就是 x + dx[(dir+3)%4],y + dy[(dir+3)%4], dir
其实只要我们的dx,dy数组设置的方向是逆时针(因为我们设置的东西南北就是逆时针设置的),就有类似的公式,每一种都是可以的。
这些东西知道了之后这道题就很简单了,套模板就行了。
—————————————————————————————————————————————————————————————————————————————
不过这道题有一个灰常隐蔽的逻辑陷阱。我们原来写的bfs,要求是某一个状态标记成true之后就不再访问它,因为后来如果再访问到这个状态肯定就不是最短路径了。
所以我刚开始这样写
if (is_legal(x, y) && G[x][y] != '#' && !vis[x][y][dir]){
......
}
我的意思是,如果这个状态坐标合法,并且不是墙,而且没访问过。我们才能访问这个状态。发现什么问题了吗?加入我们现在左边是墙,右边可以走,但是右边已经被访问过,前面也可以走,并且没有被访问过。按照我原来的写法,现在我们应该直走。但是按照题意,优先左右,所以我们应该往右走。其实我们只要将判断条件稍微调整一下就可以了。
if (is_legal(xx, yy) && G[xx][yy] != '#'){
......
if (!vis[xx][yy][ddir]){
......
}
}
现在的意思是我们看右边可以走,我们先走过去,然后发现被标记过了,这时候我们就停止继续访问,因为接下来的路径肯定不是最短的。
可能只有我自己才会有这么奇怪的错误吧。。。。。。
—————————————————————————————————————————————————————————————————————————————
代码如下:
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 205;
char G[maxn][maxn];
int d[maxn][maxn], vis[maxn][maxn][5];
int n, m, ans;
const int dx[] = {0, 1, 0, -1};//左下右上
const int dy[] = {-1, 0, 1, 0};
struct Node{
int x, y, dir;
Node(int a, int b, int c){x = a; y = b; dir = c;}
Node();
};
bool is_legal(int x, int y){
if (x >= 0 && x < n && y >= 0 && y < m)
return true;
return false;
}
bool is_end(int x, int y){
if (x == 0 || x == n - 1 || y == 0 || y == m - 1)
return true;
return false;
}
void bfs(int sx, int sy){
if (is_end(sx, sy)){
printf("0\n");
return;
}
memset(vis, 0, sizeof(vis));
memset(d, INF, sizeof(d));
queue<Node> Q;
Q.push(Node(sx, sy, 0));
Q.push(Node(sx, sy, 1));
Q.push(Node(sx, sy, 2));
Q.push(Node(sx, sy, 3));
vis[sx][sy][0] = 1;
vis[sx][sy][1] = 1;
vis[sx][sy][2] = 1;
vis[sx][sy][3] = 1;
d[sx][sy] = 0;
int has_ans = 0;
while (!Q.empty()){
Node cur = Q.front();
Q.pop();
int x = cur.x, y = cur.y, dir = cur.dir;
int ok = 0;//ok = 0表示左右都不能走,否则表示左右至少有一个是可以走的
int xx = x + dx[dir], yy = y + dy[dir], ddir = (dir + 1) % 4;//左
if (is_legal(xx, yy) && G[xx][yy] != '#'){
ok = 1;
if (!vis[xx][yy][ddir]){
if (is_end(xx, yy)) {printf("%d\n", d[x][y] + 1); has_ans = 1; break;}
ok = 1;
vis[xx][yy][ddir] = 1;
d[xx][yy] = d[x][y] + 1;
Q.push(Node(xx, yy, ddir));
}
}
xx = x + dx[(dir+2)%4]; yy = y + dy[(dir+2)%4]; ddir = (dir + 3) % 4;//右
if (is_legal(xx, yy) && G[xx][yy] != '#'){
ok = 1;
if (!vis[xx][yy][ddir]){
if (is_end(xx, yy)) {printf("%d\n", d[x][y] + 1); has_ans = 1; break;}
vis[xx][yy][ddir] = 1;
d[xx][yy] = d[x][y] + 1;
Q.push(Node(xx, yy, ddir));
}
}
if (!ok){//左右堵死,只能前进
xx = x + dx[(dir+3)%4]; yy = y + dy[(dir+3)%4];
if (is_legal(xx, yy) && G[xx][yy] != '#' && !vis[xx][yy][dir]){
if (is_end(xx, yy)) {printf("%d\n", d[x][y] + 1); has_ans = 1; break;}
vis[xx][yy][dir] = 1;
d[xx][yy] = d[x][y] + 1;
Q.push(Node(xx, yy, dir));
}
}
}
if (!has_ans) printf("-1\n");
}
int main()
{
//freopen("1.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++)
scanf("%s", G[i]);
int sx, sy;//找出开始位置
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (G[i][j] == '@'){sx = i; sy = j;}
bfs(sx, sy);
}
return 0;
}