AOJ 1622 Go around the Labyrinth

解法

右手法かフロー(最大流)で解けるが、今回はフローで解く。
フロー解は割と一発ネタなところがある。

まず、各マスを2つの頂点に倍化(入力ノード、出力ノード)し、これら2つの頂点の間に辺をはる。
このときのコストは、4隅以外は1、4隅は2とする。
また、隣接マスの間に、コスト1の無向辺を張る。出力ノードから入力ノードに張ること。

こうして作ったグラフをコピーして2つ用意し、g1, g2 とする。
g1 における左上から右下への最大流が2であり、かつ、g2 における左下から右上への最大流が2であるとき、"YES" となる。

これでうまくいくのは、直感的には、4つの disjoint なパスを重ね合わせたときに、パスの外周を回るように移動すれば実現できるからである。

N, M <= 50 のグリッドグラフなのでフローでも間に合う。
右手法だと N, M <= 1000 とかでも解けるが、フロー解は実装で失敗するところが皆無なので、どっちを選択するかは好みの問題かもしれない。

ソースコード

#include <bits/stdc++.h>
using namespace std;

template <typename Edge>
class graph {
    using graph_t = std::vector<std::vector<Edge>>;
public:
    using reference = std::vector<Edge>&;
    using const_reference = std::vector<Edge> const&;
    using iterator = typename graph_t::iterator;
    using const_iterator = typename graph_t::const_iterator;
    using reverse_iterator = typename graph_t::reverse_iterator;

    graph() : g() {}
    graph(int n) : g(n) {}

    reference operator[](int x) { return g[x]; }
    const_reference operator[](int x) const { return g[x]; }

    iterator begin() { return std::begin(g); }
    const_iterator begin() const { return std::begin(g); }
    iterator end() { return std::end(g); }
    const_iterator end() const { return std::end(g); }
    reverse_iterator rbegin() { return std::rbegin(g); }
    reverse_iterator rend() { return std::rend(g); }

    int size() const { return g.size(); }

    void add_node(std::vector<Edge> es) {
        g.push_back(std::move(es));
    }

private:
    std::vector<std::vector<Edge>> g;
};

template <typename Capacity>
struct capacity_edge {
    using capacity_type = Capacity;
    int to, rev;
    capacity_type cap;
    capacity_edge(int t, int r, capacity_type c) : to(t), rev(r), cap(c) {}
};

template <typename Capacity>
using capacity_graph = graph<capacity_edge<Capacity>>;

template <typename Capacity>
void add_edge(capacity_graph<Capacity>& g, int from, int to, Capacity cap) {
    g[from].emplace_back(to, static_cast<int>(g[to].size()), cap);
    g[to].emplace_back(from, static_cast<int>(g[from].size() - 1), Capacity{0});
}

template <typename Edge, typename Capacity = typename Edge::capacity_type>
Capacity augument(graph<Edge>& g, std::vector<bool>& used, int v, int t, Capacity f) {
    if(v == t) return f;
    used[v] = true;
    for(auto& e : g[v]) {
        if(!used[e.to] && e.cap > 0) {
            auto d = augument(g, used, e.to, t, std::min(f, e.cap));
            if(d > 0) {
                e.cap -= d;
                g[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

template <typename Edge, typename Capacity = typename Edge::capacity_type>
Capacity max_flow(graph<Edge>& g, int s, int t) {
    Capacity flow = 0;
    std::vector<bool> used(g.size(), false);
    while(true) {
        std::fill(std::begin(used), std::end(used), false);
        const auto f = augument(g, used, s, t, std::numeric_limits<Capacity>::max() / 2);
        if(f == 0) return flow;
        flow += f;
    }
}

constexpr int dx[4] = {0, 1, 0, -1};
constexpr int dy[4] = {1, 0, -1, 0};

int main() {
    int n, m;
    while(cin >> n >> m, n) {
        vector<string> s(n);
        for(int i = 0; i < n; ++i) {
            cin >> s[i];
        }

        capacity_graph<int> g1(2 * n * m);
        auto in_range = [&] (int y, int x) {
            return 0 <= y && y < n && 0 <= x && x < m;
        };
        for(int i = 0; i < n; ++i) {
            for(int j = 0; j < m; ++j) {
                if((i == 0 && j == 0) || (i == 0 && j == m - 1) || (i == n - 1 && j == 0) || (i == n - 1 && j == m - 1)) {
                    add_edge(g1, i * m + j, i * m + j + n * m, 2);
                } else {
                    add_edge(g1, i * m + j, i * m + j + n * m, 1);
                }
                if(s[i][j] == '#') continue;
                for(int k = 0; k < 4; ++k) {
                    const int ny = i + dy[k], nx = j + dx[k];
                    if(!in_range(ny, nx) || s[ny][nx] == '#') continue;
                    add_edge(g1, i * m + j + n * m, ny * m + nx, 1);
                }
            }
        }
        auto g2 = g1;

        if(max_flow(g1, 0, (n - 1) * m + m - 1) == 2 && max_flow(g2, (n - 1) * m, m - 1) == 2) {
            cout << "YES" << endl;
        } else {
            cout << "NO" << endl;
        }
    }
}