DP over a grid where the state is the boundary between the filled and unfilled regions. Used for tilings, connectivity constraints, and any grid problem where a cell’s validity depends on its neighbours.

Row-by-row profile DP

The simpler form: process one row at a time, with the state being a bitmask of which cells in the previous row are occupied or “stick out”.

Domino tiling of an grid:

// mask bit j = 1 means cell (row, j) is already filled by a vertical domino from above
long long dominoTiling(int n, int m) {
    vector<long long> dp(1 << m, 0), ndp;
    dp[0] = 1;
    for (int r = 0; r < n; r++) {
        ndp.assign(1 << m, 0);
        for (int mask = 0; mask < (1 << m); mask++) {
            if (!dp[mask]) continue;
            fill(0, mask, 0, m, dp[mask], ndp);       // recursive filler below
        }
        dp = ndp;
    }
    return dp[0];
}
 
void fill(int col, int mask, int next, int m, long long ways, vector<long long>& ndp) {
    if (col == m) { ndp[next] += ways; return; }
    if (mask >> col & 1) { fill(col + 1, mask, next, m, ways, ndp); return; }  // occupied
    fill(col + 1, mask, next | (1 << col), m, ways, ndp);                      // vertical
    if (col + 1 < m && !(mask >> (col + 1) & 1))
        fill(col + 2, mask, next, m, ways, ndp);                               // horizontal
}

— so must be small (say ). Always process along the shorter dimension.

True broken profile — cell by cell

Instead of a whole row at a time, advance one cell at a time. The state is the “staircase” boundary: bits describing the frontier, which crosses two adjacent rows.

This reduces the transition to per state, giving total with a much smaller constant, and it makes irregular constraints far easier to express.

// dp[mask] over cells in row-major order
for (int i = 0; i < n; i++)
    for (int j = 0; j < m; j++) {
        ndp.assign(1 << m, 0);
        for (int mask = 0; mask < (1 << m); mask++) {
            if (!dp[mask]) continue;
            bool filled = mask >> j & 1;
            if (filled) ndp[mask ^ (1 << j)] += dp[mask];        // already covered
            else {
                if (i + 1 < n) ndp[mask | (1 << j)] += dp[mask]; // place vertical
                if (j + 1 < m && !(mask >> (j+1) & 1))
                    ndp[mask | (1 << (j+1))] += dp[mask];        // place horizontal
            }
        }
        dp = ndp;
    }

The bit for column means “cell is already covered by something placed earlier”.

Beyond tilings: connectivity profiles

Some grid problems need the profile to record not just occupancy but which frontier cells are connected to each other — for counting Hamiltonian circuits, connected regions, or plugin-style problems. The state becomes a minimum representation (a canonical labelling of the connectivity classes on the frontier), stored in a hash map because the state space is sparse.

This is the “plug DP” / “connection profile” technique. It is heavy machinery, but it is what makes problems like “count simple cycles covering every cell of a grid” tractable.

When to reach for it

Signal
Grid with one small dimension ()
Placing shapes that span cells (dominoes, L-trominoes, kings)
Constraint between adjacent cells
Counting tilings or configurations
Both dimensions large✘ — look for another structure
Constraint is global (connectivity of the whole board)needs the connectivity-profile variant

Alternatives to check first

  • Independent rows. If a row’s validity depends only on that row, it is a simple DP with a precomputed compatibility table between consecutive masks.
  • Matrix exponentiation. If the transition between row masks is a fixed matrix, matrix exponentiation handles up to in .
  • Closed forms. Domino tilings of are Fibonacci; satisfies a short linear recurrence. Compute small cases and check OEIS before writing profile DP.
  • Matrix-Tree / permanents. Some tiling counts have determinant formulas (the Kasteleyn / FKT algorithm counts perfect matchings in planar graphs in polynomial time — including domino tilings of any planar region).

See also: Bitmask DP · Matrix Exponentiation · Designing States