P1825 [USACO11OPEN]Corn Maze S

P1825 [USACO11OPEN]Corn Maze S

标记还是不标记?这是个问题.

直接看得出来是bfs,只不过遇到传送装置要特殊处理.

最初的想法是,每当遍历到一个为传送门的新格子时,而该格子本身不标记,将该格子传送到的格子标记为visited.在这个基础上就可以当作普通bfs来做了.

结果是WA和AC参半.这样做的漏洞在于使得一个传送装置只能单向传送一次,而这是会漏解的,如:

#####.#
#@A=#A#
#######

→↑↓→即可到达终点,而上述做法导致此情况无解.

正确的方法是遇到传送装置时标记该格子,而不标记传送到的格子.在这个基础上当成普通bfs即可AC,

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;

int n, m, sx, sy, fx, fy;
int trans[30][2][2];
int dx[4] = {0, 0, -1, 1}, dy[4] = {-1, 1, 0, 0};
char s[310][310];
bool vis[310][310];
struct S {
    int x, y, t;
};
queue<struct S> que;

int main() {
    // freopen("out.txt", "w", stdout);
    // freopen("in.txt", "r", stdin);
    char tmp;
    cin >> n >> m;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++) {
            cin >> tmp;
            if (tmp == '@') {
                sx = i;
                sy = j;
            } else if (tmp == '=') {
                fx = i;
                fy = j;
            } else if (tmp != '#' && tmp != '.') {
                if (!trans[tmp - 'A'][0][0]) {
                    trans[tmp - 'A'][0][0] = i;
                    trans[tmp - 'A'][0][1] = j;
                } else {
                    trans[tmp - 'A'][1][0] = i;
                    trans[tmp - 'A'][1][1] = j;
                }
            }
            s[i][j] = tmp;
        }

    que.push({sx, sy, 0});
    vis[sx][sy] = true;
    while (!que.empty()) {
        struct S p = que.front();
        que.pop();

        if (p.x == fx && p.y == fy) {
            printf("%d\n", p.t);
            break;
        }

        for (int i = 0; i < 4; i++) {
            int nx = p.x + dx[i], ny = p.y + dy[i];
            if (nx >= 0 && nx < n && ny >= 0 && ny < m && s[nx][ny] != '#' && !vis[nx][ny]) {
                if (s[nx][ny] == '.' || s[nx][ny] == '=') {
                        que.push({nx, ny, p.t + 1});
                        // printf("!!!(%d,%d) %d\n", nx, ny, p.t + 1);
                        vis[nx][ny] = true;
                } else {  // transport
                    vis[nx][ny] = true;
                    int ind = s[nx][ny] - 'A';
                    if (trans[ind][0][0] == nx && trans[ind][0][1] == ny) {
                        nx = trans[ind][1][0];
                        ny = trans[ind][1][1];
                    } else {
                        nx = trans[ind][0][0];
                        ny = trans[ind][0][1];
                    }
                    // if (!vis[nx][ny]) {
                        que.push({nx, ny, p.t + 1});
                        // printf("!!!(%d,%d) %d\n", nx, ny, p.t + 1);
                        // vis[nx][ny] = true;
                    // }
                }
            }
        }
    }

    return 0;
}
View Code

 此外,实践表明如果遇到传送门时不对任何格子做标记,也不检测目标格子是否已经标记而直接push到队列里面的话仍然可以AC.此时可以想象到队列里面有几个点传送来传送去,他们的step不断递增并且不会跳出这个循环,并不会对结果产生影响,只是让搜索的效率有所下降,这样的点越多效率损耗越大.

这道题最多只有26个传送门,所以照常AC了.

所以说如果实在不确定(懒得想)标记与否,可以牺牲一点效率换取稳定性.

 
posted @ 2020-11-30 22:07  goverclock  阅读(220)  评论(0编辑  收藏  举报