EOJ 1780 Escape bfs

这道题就是一道单纯的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;
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值