ICPC 2017 国内予選: G. 迷宮を一周

,

チームメンバーの助けを多いに借りて、いい感じに実装したらあまりよく分からないまま通った。

solution

左手法、つまり壁に沿って一周するのでよい。$O(HW)$だと思う。

注意するのは次のような場合。不用意に細道に入ってはいけない。

...#.#...
...#.#...
...#.#...
...#.#...
...#.#...
.........
.........
.........

いい感じに実装する。

implementation

#include <cassert>
#include <functional>
#include <iostream>
#include <vector>
#define repeat(i, n) for (int i = 0; (i) < (n); ++ (i))
using namespace std;

const int dx[] = {0, 1, 0, -1}; // up right down left
const int dy[] = {-1, 0, 1, 0};

bool solve(int h, int w, const vector<string> & f) {
    auto is_on_field = [&](int y, int x) {
        return 0 <= x and x < w and 0 <= y and y < h;
    };
    vector<vector<bool> > visited(h, vector<bool>(w));
    function<bool (int, int, int, int)> go = [&](int y, int x, int tresuare, int left_hand) {
        assert (is_on_field(y, x) and f[y][x] == '.');
        if (y == 0     and x == w - 1 and tresuare == 0) tresuare += 1;
        if (y == h - 1 and x == w - 1 and tresuare == 1) tresuare += 1;
        if (y == h - 1 and x ==     0 and tresuare == 2) tresuare += 1;
        if (y == 0     and x ==     0 and tresuare == 3) return true;
        if (visited[y][x]) return false;
        visited[y][x] = true;
        for (int rotate = -1; rotate <= 1; ++ rotate) {
            int dir = (left_hand + rotate + 1) % 4;
            int ny = y + dy[dir];
            int nx = x + dx[dir];
            if (is_on_field(ny, nx) and f[ny][nx] == '.') {
                if (go(ny, nx, tresuare, (left_hand + rotate + 4) % 4)) return true;
            }
        }
        return false;
    };
    return go(0, 0, 0, 0);
}

int main() {
    while(true) {
        int h, w; cin >> h >> w;
        if (h == 0 and w == 0) break;
        vector<string> f(h);
        repeat (y, h) cin >> f[y];
        bool result = solve(h, w, f);
        cout << (result ? "YES" : "NO") << endl;
    }
}