All Euler problems
Project Euler

Crack-free Walls

Consider building a wall that is 32 units wide and 10 units tall using bricks of width 2 and width 3, all of height 1. A wall is crack-free if the gaps between horizontally adjacent bricks never li...

Source sync May 21, 2026
Problem #0215
Level Level 07
Solved By 4,184
Languages C++, Python
Answer 806844323190414
Length 412 words
dynamic_programminglinear_algebragraph

Problem Statement

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

Consider the problem of building a wall out of \(2 \times 1\) and \(3 \times 1\) bricks (\(\text {horizontal} \times \text {vertical}\) dimensions) such that, for extra strength, the gaps between horizontally-adjacent bricks never line up in consecutive layers, i.e. never form a "running crack".

For example, the following \(9 \times 3\) wall is not acceptable due to the running crack shown in red:

PIC

There are eight ways of forming a crack-free \(9 \times 3\) wall, written \(W(9,3) = 8\).

Calculate \(W(32,10)\).

Problem 215: Crack-free Walls

Mathematical Development

Definition. A row tiling of width ww is an ordered sequence of bricks of width 2 or 3 whose widths sum to ww. The crack set C(r)C(r) of a row tiling rr is the set of interior partial sums, namely the joint positions between consecutive bricks, excluding 0 and ww.

Theorem 1 (Consecutive-layer characterization). A wall with rows r1,…,rhr_1, \ldots, r_h is crack-free if and only if

C(ri)∩C(ri+1)=∅for every 1≤i<h.C(r_i) \cap C(r_{i+1}) = \emptyset \qquad \text{for every } 1 \leq i < h.

Proof. This is exactly the condition stated in the problem: no crack position may line up in two consecutive layers. A position xx lines up between rows rir_i and ri+1r_{i+1} precisely when x∈C(ri)∩C(ri+1)x \in C(r_i) \cap C(r_{i+1}). □\square

Lemma 1 (Row tiling count). The number of row tilings of width 32 with bricks of width 2 and 3 equals

∑(a+ba)\sum \binom{a+b}{a}

over all (a,b)∈Z≥02(a, b) \in \mathbb{Z}_{\geq 0}^2 satisfying 2a+3b=322a + 3b = 32. The valid pairs are

(16,0), (13,2), (10,4), (7,6), (4,8), (1,10).(16,0),\ (13,2),\ (10,4),\ (7,6),\ (4,8),\ (1,10).

Proof. A tiling with aa bricks of width 2 and bb bricks of width 3 is an ordering of a+ba+b objects with aa identical 2-bricks and bb identical 3-bricks. The number of such orderings is (a+ba)\binom{a+b}{a}. The width equation gives the listed pairs. □\square

Theorem 2 (Dynamic programming on the compatibility graph). Let RR be the set of all row tilings. Define f(r,k)f(r, k) as the number of crack-free walls of height kk whose top row is rr. Then

  • Base case: f(r,1)=1f(r, 1) = 1 for all r∈Rr \in R.
  • Recurrence: f(r,k)=∑r′∈RC(r)∩C(r′)=∅f(r′,k−1).f(r, k) = \sum_{\substack{r' \in R \\ C(r) \cap C(r') = \emptyset}} f(r', k-1).
  • Answer: ∑r∈Rf(r,10).\sum_{r \in R} f(r, 10).

Proof. A valid wall of height kk ending in row rr is obtained by taking a valid wall of height k−1k-1 ending in some compatible row r′r' and placing rr above it. Every valid wall arises uniquely in this way. □\square

Editorial

The geometry disappears once each row is represented by its crack positions. A row is compatible with another row exactly when their crack sets are disjoint, so the wall problem becomes a path-counting problem in a finite compatibility graph whose vertices are the possible row tilings.

That graph is small enough to build explicitly. After generating all width-32 rows, precompute which pairs are compatible. Then dynamic programming over the wall height counts how many ways there are to end with each possible top row. Summing those counts after 10 layers gives the total number of crack-free walls.

Pseudocode

Generate every row tiling of width 32.
For each row, store the set or bitmask of its crack positions.

Build the compatibility list:
    row i is adjacent to row j
    if their crack sets are disjoint.

Set dp[i] = 1 for every row i, representing walls of height 1.

Repeat 9 times:
    Create a new array next filled with 0.
    For each row i:
        For each compatible predecessor j of row i:
            next[i] += dp[j]
    Replace dp by next.

Return the sum of all entries of dp.

Complexity Analysis

Let mm be the number of width-32 row tilings.

  • Time: O(m2)O(m^2) to build compatibility, then O(10m2)O(10m^2) for the height DP.
  • Space: O(m2)O(m^2) for the compatibility graph and O(m)O(m) for the DP arrays.

Answer

806844323190414\boxed{806844323190414}

Code

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

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

int W = 32;
int H = 10;

vector<vector<int>> rows; // each row is a set of crack positions

void generate(int pos, vector<int>& cracks) {
    if (pos == W) {
        rows.push_back(cracks);
        return;
    }
    // Try placing a 2-brick
    if (pos + 2 <= W) {
        if (pos + 2 < W) cracks.push_back(pos + 2);
        generate(pos + 2, cracks);
        if (pos + 2 < W) cracks.pop_back();
    }
    // Try placing a 3-brick
    if (pos + 3 <= W) {
        if (pos + 3 < W) cracks.push_back(pos + 3);
        generate(pos + 3, cracks);
        if (pos + 3 < W) cracks.pop_back();
    }
}

int main() {
    vector<int> cracks;
    generate(0, cracks);

    int m = rows.size();

    // Convert crack positions to bitmask for fast compatibility check
    vector<unsigned int> mask(m, 0);
    for (int i = 0; i < m; i++) {
        for (int c : rows[i]) {
            mask[i] |= (1u << c);
        }
    }

    // Build compatibility list
    vector<vector<int>> compat(m);
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < m; j++) {
            if ((mask[i] & mask[j]) == 0) {
                compat[i].push_back(j);
            }
        }
    }

    // DP
    vector<long long> dp(m, 1); // height 1: each row has 1 way

    for (int h = 2; h <= H; h++) {
        vector<long long> ndp(m, 0);
        for (int i = 0; i < m; i++) {
            for (int j : compat[i]) {
                ndp[i] += dp[j];
            }
        }
        dp = ndp;
    }

    long long answer = 0;
    for (int i = 0; i < m; i++) {
        answer += dp[i];
    }

    cout << answer << endl;
    return 0;
}