All Euler problems
Project Euler

Skew-cost Coding

Build a prefix-free binary code for N = 10^9 symbols. A 0 bit costs 1 and a 1 bit costs 4. The cost of a codeword is the sum of the costs of its bits, and the total cost is the sum over all codewor...

Source sync May 21, 2026
Problem #0219
Level Level 11
Solved By 1,724
Languages C++, Python
Answer 64564225042
Length 391 words
optimizationgreedybrute_force

Problem Statement

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

Let $A$ and $B$ be bit strings (sequences of 0's and 1's).

If $A$ is equal to the leftmost length($A$) bits of $B$, then $A$ is said to be a prefix of $B$.

For example, 00110 is a prefix of 001101001, but not of 00111 or 100110.

A prefix-free code of size n is a collection of n distinct bit strings such that no string is a prefix of any other. For example, this is a prefix-free code of size 6: $$0000, 0001, 001, 01, 10, 11$$ Now suppose that it costs one penny to transmit a '0' bit, but four pence to transmit a '1'.

Then the total cost of the prefix-free code shown above is 35 pence, which happens to be the cheapest possible for the skewed pricing scheme in question.

In short, we write $\operatorname{Cost}(6) = 35$.

What is $\operatorname{Cost}(109)$ ?

Problem 219: Skew-cost Coding

Mathematical Development

Definition. A prefix-free code corresponds to a binary tree whose leaves are the codewords. The cost of a leaf is the weighted path length from the root, where moving left adds 1 and moving right adds 4.

Theorem 1 (Greedy optimality). An optimal code is obtained by repeatedly splitting a leaf of minimum current cost.

Proof. Suppose some optimal tree splits a leaf of cost c2c_2 while leaving another leaf of smaller cost c1<c2c_1 < c_2 unsplit. Swapping those choices decreases the total cost by

[(c1+1)+(c1+4)−c1]−[(c2+1)+(c2+4)−c2]=c1−c2<0,\bigl[(c_1+1)+(c_1+4)-c_1\bigr] - \bigl[(c_2+1)+(c_2+4)-c_2\bigr] = c_1 - c_2 < 0,

contradicting optimality. Therefore the next split must always come from a minimum-cost leaf. □\square

Lemma 1 (Cost accounting). Splitting a leaf of cost cc removes that leaf and creates two leaves of costs c+1c+1 and c+4c+4. The net increase in total cost is

(c+1)+(c+4)−c=c+5.(c+1) + (c+4) - c = c + 5.

The number of leaves increases by 1.

Theorem 2 (Bulk processing of equal-cost leaves). If the current minimum cost is cc and there are kk leaves of that cost, then processing them all together is equivalent to kk consecutive greedy splits. The histogram update is:

  • remove kk leaves of cost cc
  • add kk leaves of cost c+1c+1
  • add kk leaves of cost c+4c+4

Proof. All kk leaves are tied for minimum cost, so the greedy rule may split them in any order. Each split has the same local effect, and the kk effects add linearly. □\square

Editorial

The tree interpretation makes the greedy step completely local. Splitting a leaf of cost cc replaces it by two children of costs c+1c+1 and c+4c+4, so the total cost rises by c+5c+5. The exchange argument shows that delaying a cheaper leaf in favor of a more expensive one can only make the final total worse.

That means we never need the full tree structure. A histogram of how many leaves exist at each cost is enough. Repeatedly take the smallest cost bucket, split as many leaves from that bucket as are still needed, update the counts of the c+1c+1 and c+4c+4 buckets, and continue until the tree has exactly 10910^9 leaves.

Pseudocode

Create a sorted map frequency with one entry:
    cost 0 appears once.

leaf_count = 1
total_cost = 0

While leaf_count < N:
    Let c be the smallest cost present in the map.
    Let available be the number of leaves of cost c.

    need = N - leaf_count
    splits = min(available, need)

    total_cost += splits * (c + 5)
    leaf_count += splits

    Add splits leaves to cost c + 1.
    Add splits leaves to cost c + 4.

    If not all leaves of cost c were split:
        put the unused remainder back into cost c.

Return total_cost

Complexity Analysis

  • Time: O(Mlog⁡M)O(M \log M) where MM is the number of distinct cost buckets that ever become active in the histogram.
  • Space: O(M)O(M) for the histogram.

Answer

64564225042\boxed{64564225042}

Code

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

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

/*
 * Problem 219: Skew-cost Coding
 *
 * Build a prefix-free code for N = 10^9 symbols.
 * Bit '0' costs 1, bit '1' costs 4.
 * Minimize total cost of all codewords.
 *
 * Greedy: always split the cheapest codeword.
 * Splitting cost c -> produces c+1 and c+4.
 * Use histogram for bulk processing.
 *
 * Answer: 64564225042
 */

int main() {
    const long long N = 1000000000LL;

    // Histogram: cost -> count
    // Use a sorted map for efficient min access
    map<long long, long long> freq;
    freq[0] = 1;
    long long total_cost = 0;
    long long num_codes = 1;

    while (num_codes < N) {
        auto it = freq.begin();
        long long cost = it->first;
        long long count = it->second;
        freq.erase(it);

        // How many can we split? Each split adds 1 codeword.
        long long need = N - num_codes;
        long long splits = min(count, need);

        // Split 'splits' codewords of this cost
        total_cost += splits * (cost + 5);
        num_codes += splits;

        // Add children
        freq[cost + 1] += splits;
        freq[cost + 4] += splits;

        // If we didn't split all, put the rest back
        if (splits < count) {
            freq[cost] += (count - splits);
        }
    }

    cout << total_cost << endl;
    return 0;
}