Official

G - Big Banned Grid Editorial by en_translator


Consider a graph with \(HW\) vertices \((i,j)\ (1\le i\le H,1\le j\le W)\) and with edges between \((i,j)\) and \((i+1,j)\), and between \((i,j)\) and \((i,j+1)\), whose capacity is defined as:

  • \(0\) if either cell contains an obstacle, and
  • \(1\) otherwise.

The answer to the original problem is Yes if and only if the maximum flow from \((1,1)\) to \((H,W)\) is \(1\) or greater.

By the max-flow min-cut theorem, this is equivalent to that there is no weight-\(0\) cut.

Since the graph above is a planar graph, one can embed it in the plane and take the dual (specifically, by placing vertex \((i,j)\) to coordinates \((i,j)\)) to reduce the problem to a shortest path problem on the dual graph.

Notably, it is sufficient to determine if the weight of the shortest path is \(0\). Since the edge weights are non-negative, it is enough to check if the graph is connected only by the edges of weight \(0\).

This can be determined by (after removing isolated vertices appropriately) Breadth-First Search, Depth-First Search, or Disjoint Set Union (DSU).

The following is sample code. In the following sample code, we use DSU.

#include <iostream>
#include <unordered_map>

int main() {
    using namespace std;
    unsigned H, W, K;
    cin >> H >> W >> K;

    // Implement DSU with hash map
    unordered_map<unsigned long, unsigned long> parent;
    unordered_map<unsigned long, unsigned> size;

    const auto find{[&parent, &size](unsigned long i) {
        if (!parent.contains(i)) {
            parent[i] = i;
            size[i] = 1;
        }
        while (i != parent[i])
            i = parent[i] = parent[parent[i]];
        return i;
    }};
    const auto unite{[&parent, &size, &find](unsigned long i, unsigned long j) {
        i = find(i);
        j = find(j);
        if (i == j) return;
        if (size[i] < size[j]) swap(i, j);
        size[i] += size[j];
        parent[j] = i;
    }};

    // Convert from a vertex in the dual graph to the corresponding index
    const auto coordinate_to_index{[H, W](unsigned long x, unsigned long y) -> unsigned long {
        if (x == 0 || y == W)
            return 0;
        if (y == 0 || x == H)
            return 1;
        return x * W + y;
    }};

    for (unsigned i{}, x, y; i < K; ++i) {
        cin >> x >> y;
        // Make the related edge weights 0 -> add those edges to the dual graph
        if (x > 1)
            unite(coordinate_to_index(x - 1, y), coordinate_to_index(x - 1, y - 1));
        if (y > 1)
            unite(coordinate_to_index(x, y - 1), coordinate_to_index(x - 1, y - 1));
        if (x < H)
            unite(coordinate_to_index(x, y - 1), coordinate_to_index(x, y));
        if (y < W)
            unite(coordinate_to_index(x - 1, y), coordinate_to_index(x, y));
    }

    // If the dual graph is connected, then the weight of minimum cut = maximum flow is 0
    cout << (find(0) == find(1) ? "No" : "Yes") << endl;
    return 0;
}

posted:
last update: