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...
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:

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

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:
In how many ways can a $9$ by $12$ grid be tiled in this way by triominoes?
Problem 161: Triominoes
Mathematical Development
Definitions
Definition 1. A tiling of a region by triominoes is a partition of the unit squares of into triominoes such that every square belongs to exactly one triomino.
Definition 2. The broken profile at cell in a row-major traversal of a grid is the bitmask encoding which cells ahead of in the traversal order are already occupied by previously placed triominoes.
Theorem 1 (Existence and finiteness). The number of distinct tilings of the grid by triominoes is a well-defined non-negative integer.
Proof. The grid contains unit squares. Since each triomino covers exactly 3 squares and , 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.
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 from the anchor:
| Type | Offsets | Row span | Column span |
|---|---|---|---|
| L-A | 2 | 2 | |
| L-B | 2 | 2 | |
| L-C | 2 | 2 | |
| L-D | 2 | 2 | |
| I-H | 1 | 3 | |
| I-V | 3 | 1 |
Proof. Enumerate by inspection. Each L-triomino is a block with one corner removed, hence spans 2 rows and 2 columns. The I-triomino is either (horizontal) or (vertical).
Theorem 2 (Broken-profile DP correctness). Processing the grid cell-by-cell in row-major order, the state at each cell is fully characterized by a bitmask of width at most bits. The number of distinct tilings equals the value after processing the final cell, where the bitmask indicates all frontier cells are unoccupied.
Proof. We process cells in the order . At cell , any triomino anchored at occupies cells at offsets listed in Lemma 1. The maximum forward reach of any placement in row-major order is positions. However, we only need to track which cells ahead of the current position are already filled.
Since each triomino offset satisfies in the worst case (I-V type), and the current cell is always bit 0, the profile is a bitmask of at most bits (after the current cell is shifted out at each step). This gives 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 cells, counts the tilings where every cell is covered exactly once.
Lemma 2 (State space bound). The number of reachable profiles at any step is at most , 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.
Editorial
The implementation uses broken-profile dynamic programming, scanning the board in row-major order and remembering only which future cells near the current frontier are already occupied. That is enough because every legal triomino placement is anchored at the first uncovered cell and extends only a small, fixed distance ahead. The state therefore collapses to a bitmask rather than the entire partial tiling.
At each cell there are only two situations. If the low bit of the mask says the cell is already covered, the algorithm simply shifts the mask and advances. Otherwise it tries each of the six triomino shapes anchored at that position, checks whether every required square stays inside the grid and is currently free, and then marks those squares in the mask. Summing the transition counts over all cells gives the number of complete tilings, with mask 0 at the end representing a fully covered board.
Pseudocode
Start with one DP state: an empty frontier mask with count 1.
Process the board cell by cell in row-major order.
For each current mask:
If the current cell is already occupied in the mask:
shift the mask right by one position and carry its count forward.
Otherwise:
try each anchored triomino shape.
Reject a shape if any covered square leaves the board
or if any corresponding bit is already set in the mask.
For every valid shape, set the occupied bits, shift right by one,
and add the count into the next DP table.
After the last cell, return the count stored for the empty mask.
Complexity Analysis
Time. Each of the cells is processed once. At each cell, we iterate over all reachable profiles (at most in theory) and try at most 6 placements. This gives worst-case . In practice the reachable state count is far smaller.
Space. for the hash map at any given step.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#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;
}
"""
Project Euler Problem 161: Triominoes
Count the number of ways to tile a 9x12 grid with triominoes
(both L-shaped and I-shaped) using broken-profile dynamic programming.
"""
from collections import defaultdict
def solve():
ROWS, COLS = 9, 12
# All 6 triomino placements anchored at (0,0) in row-major order:
# L-A: (0,0),(0,1),(1,0) L-B: (0,0),(0,1),(1,1)
# L-C: (0,0),(1,0),(1,1) L-D: (0,0),(1,-1),(1,0)
# I-H: (0,0),(0,1),(0,2) I-V: (0,0),(1,0),(2,0)
triominoes = [
[(0, 0), (0, 1), (1, 0)],
[(0, 0), (0, 1), (1, 1)],
[(0, 0), (1, 0), (1, 1)],
[(0, 0), (1, -1), (1, 0)],
[(0, 0), (0, 1), (0, 2)],
[(0, 0), (1, 0), (2, 0)],
]
dp = defaultdict(int)
dp[0] = 1
for r in range(ROWS):
for c in range(COLS):
ndp = defaultdict(int)
for mask, ways in dp.items():
if mask & 1:
ndp[mask >> 1] += ways
else:
for tri in triominoes:
ok = True
nmask = mask
for dr, dc in tri:
nr, nc = r + dr, c + dc
if nr < 0 or nr >= ROWS or nc < 0 or nc >= COLS:
ok = False
break
bit = dr * COLS + dc
if bit < 0 or nmask & (1 << bit):
ok = False
break
nmask |= 1 << bit
if ok:
ndp[nmask >> 1] += ways
dp = ndp
print(dp.get(0, 0))
solve()
