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;
}




### EOJ2654 题目解析与解法建议 EOJ2654 是华东师范大学在线评测系统(EOJ)中的一道编程题目。根据EOJ系统的常见题型和风格,这类题目通常涉及基础的数据结构操作、算法实现或数学建模。虽然没有直接提及EOJ2654的具体题目内容,但可以从EOJ系统的典型题目特征出发,结合常见的编程题类型,给出可能的解题思路与实现方法。 #### 题目类型推测 EOJ系统中常见的题目包括但不限于: - 数值计算与数学建模 - 字符串处理与模式匹配 - 数据结构操作(如栈、队列、链表等) - 图论与搜索算法 - 动态规划与贪心策略 根据题目编号(2654)以及EOJ的出题规律,该题可能属于**中等难度**,可能涉及**数学建模**或**字符串处理**。 #### 示例解法思路(假设题目为字符串处理) 假设题目要求判断某个字符串是否为回文串(Palindrome),可以采用如下C++实现: ```cpp #include <iostream> #include <string> #include <algorithm> using namespace std; int main() { string s; cin >> s; string rev = s; reverse(rev.begin(), rev.end()); if (s == rev) { cout << "Yes" << endl; } else { cout << "No" << endl; } return 0; } ``` #### 示例解法思路(假设题目为数学建模) 若题目要求求解某个数列的第n项,例如斐波那契数列,可以使用递归或迭代方法。以下是迭代方法的实现: ```cpp #include <iostream> using namespace std; long long fib(int n) { long long a = 0, b = 1, c; if (n == 0) return a; for (int i = 2; i <= n; i++) { c = a + b; a = b; b = c; } return b; } int main() { int n; cin >> n; cout << fib(n) << endl; return 0; } ``` #### 注意事项 在EOJ系统中,需要注意以下几点以避免运行时错误或超时: 1. **输入输出效率**:对于大规模输入数据,建议使用`scanf`和`printf`代替`cin`和`cout`以提高效率[^1]。 2. **数据类型选择**:在涉及大数运算时,应使用`long long`等大整数类型以防止溢出[^4]。 3. **边界条件处理**:特别注意输入数据的边界情况,如最小值、最大值等。 4. **代码简洁性与可读性**:保持代码结构清晰,避免不必要的复杂逻辑,以便调试与优化。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值