All Euler problems
Project Euler

Triominoes

A triomino is a shape consisting of three unit squares joined edge-to-edge. There are exactly two combinatorial types: the I-triomino (three collinear squares) and the L-triomino (three squares for...

Source sync Apr 19, 2026
Problem #0161
Level Level 09
Solved By 2,551
Languages C++, Python
Answer 20574308184277971
Length 669 words
dynamic_programmingbrute_forcecombinatorics

Problem Statement

This archive keeps the full statement, math, and original media on the page.

A triomino is a shape consisting of three squares joined via the edges. There are two basic forms:

Problem illustration

If all possible orientations are taken into account there are six:

Problem illustration

Any $n$ by $m$ grid for which $n \times m$ is divisible by $3$ can be tiled with triominoes.

If we consider tilings that can be obtained by reflection or rotation from another tiling as different there are $41$ ways a $2$ by $9$ grid can be tiled with triominoes:

Problem animation

In how many ways can a $9$ by $12$ grid be tiled in this way by triominoes?

Problem 161: Triominoes

Definitions

Definition 1. A tiling of a region RR by triominoes is a partition of the unit squares of RR into triominoes such that every square belongs to exactly one triomino.

Definition 2. The broken profile at cell (r,c)(r, c) in a row-major traversal of a grid is the bitmask encoding which cells ahead of (r,c)(r, c) in the traversal order are already occupied by previously placed triominoes.

Mathematical Development

Theorem 1 (Existence and finiteness). The number of distinct tilings of the 9×129 \times 12 grid by triominoes is a well-defined non-negative integer.

Proof. The grid contains 9×12=1089 \times 12 = 108 unit squares. Since each triomino covers exactly 3 squares and 31083 \mid 108, the necessary divisibility condition is satisfied. The set of valid tilings is a subset of the finite set of all ways to partition 108 cells into groups of 3, hence is finite. \square

Lemma 1 (Placement geometry). Each of the 6 triomino placement types, anchored at the topmost-leftmost cell in row-major order, occupies cells with the following offsets (dr,dc)(dr, dc) from the anchor:

TypeOffsetsRow spanColumn span
L-A(0,0),(0,1),(1,0)(0,0),\,(0,1),\,(1,0)22
L-B(0,0),(0,1),(1,1)(0,0),\,(0,1),\,(1,1)22
L-C(0,0),(1,0),(1,1)(0,0),\,(1,0),\,(1,1)22
L-D(0,0),(1,1),(1,0)(0,0),\,(1,{-1}),\,(1,0)22
I-H(0,0),(0,1),(0,2)(0,0),\,(0,1),\,(0,2)13
I-V(0,0),(1,0),(2,0)(0,0),\,(1,0),\,(2,0)31

Proof. Enumerate by inspection. Each L-triomino is a 2×22 \times 2 block with one corner removed, hence spans 2 rows and 2 columns. The I-triomino is either 1×31 \times 3 (horizontal) or 3×13 \times 1 (vertical). \square

Theorem 2 (Broken-profile DP correctness). Processing the 9×129 \times 12 grid cell-by-cell in row-major order, the state at each cell is fully characterized by a bitmask of width at most W=COLS=12W = \text{COLS} = 12 bits. The number of distinct tilings equals the value dp[0]\mathrm{dp}[0] after processing the final cell, where the bitmask 00 indicates all frontier cells are unoccupied.

Proof. We process cells in the order (0,0),(0,1),,(0,11),(1,0),,(8,11)(0,0), (0,1), \ldots, (0,11), (1,0), \ldots, (8,11). At cell (r,c)(r,c), any triomino anchored at (r,c)(r,c) occupies cells at offsets listed in Lemma 1. The maximum forward reach of any placement in row-major order is max(212+0,112+1,012+2)=24\max(2 \cdot 12 + 0, \, 1 \cdot 12 + 1, \, 0 \cdot 12 + 2) = 24 positions. However, we only need to track which cells ahead of the current position are already filled.

Since each triomino offset (dr,dc)(dr, dc) satisfies drCOLS+dc212=24dr \cdot \text{COLS} + dc \leq 2 \cdot 12 = 24 in the worst case (I-V type), and the current cell is always bit 0, the profile is a bitmask of at most COLS\text{COLS} bits (after the current cell is shifted out at each step). This gives S2COLS|\mathcal{S}| \leq 2^{\text{COLS}} possible states.

At each cell, if bit 0 of the profile is set (cell already filled), we shift right by 1 (skip). Otherwise, we try all 6 placements whose cells are within bounds and unoccupied, marking the corresponding bits. The transition is deterministic given the placement choice, and the total count is accumulated additively.

After processing all 108108 cells, dp[0]\mathrm{dp}[0] counts the tilings where every cell is covered exactly once. \square

Lemma 2 (State space bound). The number of reachable profiles at any step is at most 2COLS=212=40962^{\text{COLS}} = 2^{12} = 4096, but empirically is much smaller due to consistency constraints on partial tilings.

Proof. The upper bound follows from the bitmask width. The reduction occurs because many bitmask patterns (e.g., isolated single empty cells surrounded by filled cells) cannot arise from valid partial placements. \square

Editorial

Count the number of ways to tile a 9x12 grid with triominoes (both L-shaped and I-shaped) using broken-profile dynamic programming. We else. Finally, and the corresponding bits in mask are 0.

Pseudocode

else
and the corresponding bits in mask are 0

Complexity Analysis

Time. Each of the 9×12=1089 \times 12 = 108 cells is processed once. At each cell, we iterate over all reachable profiles (at most S212=4096|\mathcal{S}| \leq 2^{12} = 4096 in theory) and try at most 6 placements. This gives worst-case O(10840966)=O(2,654,208)O(108 \cdot 4096 \cdot 6) = O(2{,}654{,}208). In practice the reachable state count is far smaller.

Space. O(S)=O(212)O(|\mathcal{S}|) = O(2^{12}) for the hash map at any given step.

Answer

20574308184277971\boxed{20574308184277971}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_161/solution.cpp
#include <bits/stdc++.h>
using namespace std;

/*
 * Project Euler Problem 161: Triominoes
 *
 * Count the number of ways to tile a 9x12 grid with triominoes
 * (L-shaped and I-shaped) via broken-profile dynamic programming.
 */

static const int ROWS = 9;
static const int COLS = 12;

// 6 triomino placements: offsets (dr, dc) from anchor cell
static const int TRI[6][3][2] = {
    {{0, 0}, {0, 1}, {1, 0}},   // L-A
    {{0, 0}, {0, 1}, {1, 1}},   // L-B
    {{0, 0}, {1, 0}, {1, 1}},   // L-C
    {{0, 0}, {1, -1}, {1, 0}},  // L-D
    {{0, 0}, {0, 1}, {0, 2}},   // I-horizontal
    {{0, 0}, {1, 0}, {2, 0}},   // I-vertical
};

int main() {
    unordered_map<int, long long> dp, ndp;
    dp[0] = 1;

    for (int r = 0; r < ROWS; r++) {
        for (int c = 0; c < COLS; c++) {
            ndp.clear();
            for (auto &[mask, ways] : dp) {
                if (mask & 1) {
                    ndp[mask >> 1] += ways;
                } else {
                    for (int t = 0; t < 6; t++) {
                        bool ok = true;
                        int nmask = mask;
                        for (int k = 0; k < 3; k++) {
                            int nr = r + TRI[t][k][0];
                            int nc = c + TRI[t][k][1];
                            if (nr < 0 || nr >= ROWS || nc < 0 || nc >= COLS) {
                                ok = false;
                                break;
                            }
                            int bit = TRI[t][k][0] * COLS + TRI[t][k][1];
                            if (bit < 0 || (nmask & (1 << bit))) {
                                ok = false;
                                break;
                            }
                            nmask |= 1 << bit;
                        }
                        if (ok)
                            ndp[nmask >> 1] += ways;
                    }
                }
            }
            swap(dp, ndp);
        }
    }

    cout << dp[0] << endl;
    return 0;
}