一个BFS的trick

前言

感觉这个一个典中典的做法,但是我不会()

今天第二次遇到这个问题(上一次的找不到了),所以记录一下做法。

CF2041D Drunken Maze

思路

对于在走迷宫的基础上加入“不能连续往一个方向走 \(k\) 步”限制的问题,一般做法是为 BFS 的状态设置条件。类似于

\[\{x, y, last, cnt\} \]

其中 \(x, y\) 表示位置, \(last\) 表示上一步的方向, \(cnt\) 表示在这个方向上走了多少步。所以初始状态就是 \(\{start x, starty, -1, 0\}\) ,当然在跑 BFS 的过程中要记录一下距离 \(dis\) 。相应的记录当前状态是否触发过的 \(vis\) 数组也要记录这四个属性。但是直接开四维数组容易 MLE,因此可以压缩一下状态。

代码

struct state {
    int x, y, last, cnt, dis;
};

void solve(void) {
    int n, m;
    std::cin >> n >> m;
    state start;
    int ex = 0, ey = 0;
    std::vector<std::vector<char>> G(n + 1, std::vector<char>(m + 1));
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            std::cin >> G[i][j];
            if(G[i][j] == 'S') {
                start.x = i; start.y = j;
            }
            if(G[i][j] == 'T') {
                ex = i, ey = j;
            }
        } 
    }
    start.cnt = 0; start.last = -1; start.dis = 0;
    std::vector<int> vis(n * m * 4 * 4 + 1);
    auto compress = [&](int x, int y, int d, int cnt) -> int {
        return ((x * m + y) * 4 + d) * 4 + cnt;
    };
    auto bfs = [&](void) -> int {
        std::queue<state> q;
        q.push(start);
        while(q.size()) {
            auto u = q.front(); q.pop();
            if(u.x == ex && u.y == ey) {
                return u.dis;
            }
            for(int d = 0; d < 4; d++) {
                int x = u.x + dx[d];
                int y = u.y + dy[d];
                if(x > n || x < 1 || y > m || y < 1 || G[x][y] == '#') continue;
                int cnt = u.cnt;
                if(d == u.last) {
                    if(u.cnt == 3) continue;
                    cnt++;
                } else cnt = 1;
                int id = compress(x, y, d, cnt);
                if(!vis[id]) {
                    vis[id] = true;
                    q.emplace(x, y, d, cnt, u.dis + 1);
                }
            }
        }
        return -1;
    };
    std::cout << bfs() << '\n';
}
posted @ 2025-11-04 16:18  dbywsc  阅读(4)  评论(0)    收藏  举报