All Euler problems
Project Euler

Coin Sums

In England the currency is made up of pound and pence. There are eight coins in general circulation: 1p, 2p, 5p, 10p, 20p, 50p, 100p (£1), and 200p (£2) How many different ways can £2 (200p) be mad...

Source sync Apr 19, 2026
Problem #0031
Level Level 01
Solved By 94,431
Languages C++, Python
Answer 73682
Length 454 words
dynamic_programmingsequenceanalytic_math

Problem Statement

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

In the United Kingdom the currency is made up of pound (£) and pence (p). There are eight coins in general circulation: 1p, 2p, 5p, 10p, 20p, 50p, £1 (100p), and £2 (200p).

Problem 31: Coin Sums

Mathematical Development

Definition 1. Let S={c1,c2,,ck}S = \{c_1, c_2, \ldots, c_k\} be a finite set of positive integers (coin denominations). A representation of a non-negative integer nn over SS is a tuple (j1,j2,,jk)Z0k(j_1, j_2, \ldots, j_k) \in \mathbb{Z}_{\ge 0}^k satisfying i=1kjici=n\sum_{i=1}^{k} j_i c_i = n. The change-making function p(n;S)p(n; S) counts the number of such representations.

Theorem 1 (Generating function). The change-making function satisfies

p(n;S)=[xn]i=1k11xci,p(n; S) = [x^n] \prod_{i=1}^{k} \frac{1}{1 - x^{c_i}},

where [xn][x^n] denotes extraction of the coefficient of xnx^n.

Proof. Each factor admits the geometric series expansion 11xci=j=0xjci\frac{1}{1 - x^{c_i}} = \sum_{j=0}^{\infty} x^{j c_i}, valid for x<1|x| < 1. The Cauchy product of kk such series yields

i=1k11xci=(j1,,jk)Z0kxj1c1+j2c2++jkck.\prod_{i=1}^{k} \frac{1}{1 - x^{c_i}} = \sum_{(j_1, \ldots, j_k) \in \mathbb{Z}_{\ge 0}^k} x^{j_1 c_1 + j_2 c_2 + \cdots + j_k c_k}.

The coefficient of xnx^n in this product equals the cardinality of {(j1,,jk)Z0k:i=1kjici=n}\{(j_1, \ldots, j_k) \in \mathbb{Z}_{\ge 0}^k : \sum_{i=1}^{k} j_i c_i = n\}, which is p(n;S)p(n; S) by Definition 1. \square

Lemma 1 (Recurrence). Define f(i,m)f(i, m) as the number of representations of mm using only denominations {c1,,ci}\{c_1, \ldots, c_i\}. Then:

f(i,m)={1if m=0,0if m>0 and i=0,f(i1,m)if 0<m<ci,f(i1,m)+f(i,mci)if mci.f(i, m) = \begin{cases} 1 & \text{if } m = 0, \\ 0 & \text{if } m > 0 \text{ and } i = 0, \\ f(i-1, m) & \text{if } 0 < m < c_i, \\ f(i-1, m) + f(i, m - c_i) & \text{if } m \ge c_i. \end{cases}

Proof. The case m=0m = 0 admits exactly the empty sum. The case i=0i = 0, m>0m > 0 has no coins available. For mcim \ge c_i, the representations of mm over {c1,,ci}\{c_1, \ldots, c_i\} partition into two disjoint classes:

  1. Representations with ji=0j_i = 0: these are precisely the representations of mm over {c1,,ci1}\{c_1, \ldots, c_{i-1}\}, counted by f(i1,m)f(i-1, m).
  2. Representations with ji1j_i \ge 1: subtracting one copy of cic_i gives a bijection with representations of mcim - c_i over {c1,,ci}\{c_1, \ldots, c_i\}, counted by f(i,mci)f(i, m - c_i).

These classes are exhaustive and mutually exclusive, establishing the recurrence. The case 0<m<ci0 < m < c_i forces ji=0j_i = 0, collapsing to f(i1,m)f(i-1, m). \square

Theorem 2 (Space-optimal computation). The function f(k,n)f(k, n) can be computed in O(kn)O(kn) time and O(n)O(n) space using a one-dimensional array dp[0..n]\mathrm{dp}[0..n].

Proof. Initialize dp[0]=1\mathrm{dp}[0] = 1 and dp[m]=0\mathrm{dp}[m] = 0 for 1mn1 \le m \le n. This encodes f(0,m)f(0, m) for all mm.

Claim. After processing coin cic_i (for i=1,,ki = 1, \ldots, k) via the update dp[j]+=dp[jci]\mathrm{dp}[j] \mathrel{+}= \mathrm{dp}[j - c_i] for j=ci,ci+1,,nj = c_i, c_i + 1, \ldots, n (in increasing order), we have dp[m]=f(i,m)\mathrm{dp}[m] = f(i, m) for all 0mn0 \le m \le n.

Proof of claim by induction on ii. The base case i=0i = 0 holds by initialization. Suppose dp[m]=f(i1,m)\mathrm{dp}[m] = f(i-1, m) for all mm at the start of iteration ii. Consider the update for a given jcij \ge c_i:

  • Before jj is processed, dp[j]=f(i1,j)\mathrm{dp}[j] = f(i-1, j) (not yet updated in this iteration).
  • Since jci<jj - c_i < j and we process in increasing order, dp[jci]\mathrm{dp}[j - c_i] has already been updated to f(i,jci)f(i, j - c_i).
  • After the update: dp[j]=f(i1,j)+f(i,jci)=f(i,j)\mathrm{dp}[j] = f(i-1, j) + f(i, j - c_i) = f(i, j) by Lemma 1.

For j<cij < c_i, no update occurs, so dp[j]=f(i1,j)=f(i,j)\mathrm{dp}[j] = f(i-1, j) = f(i, j) (again by Lemma 1). This completes the inductive step.

After all kk iterations, dp[n]=f(k,n)=p(n;S)\mathrm{dp}[n] = f(k, n) = p(n; S). The outer loop runs kk times, the inner loop at most nn times each, giving O(kn)O(kn) time. The array has n+1n + 1 entries, giving O(n)O(n) space. \square

Editorial

We use one-dimensional dynamic programming over the available coin denominations. The array dp[t] stores the number of ways to form the total tt, and we process the coin values in increasing order so that each unordered combination is counted exactly once rather than once per permutation. For each coin, we traverse all reachable totals from that coin value up to the target and add the number of ways obtained by appending the current coin.

Pseudocode

Algorithm: Coin Change Count by One-dimensional Dynamic Programming
Require: A target sum n ≥ 0 and an ordered list of coin denominations C = (c_1, c_2, ..., c_m).
Ensure: The number of unordered representations of n using coins from C.
1: Initialize an array dp on {0, 1, ..., n} with dp[0] ← 1 and dp[t] ← 0 for t > 0.
2: For each coin denomination c in C do:
3:     For each total t with c ≤ t ≤ n, update dp[t] ← dp[t] + dp[t - c].
4: Return dp[n].

Complexity Analysis

Proposition. For S={1,2,5,10,20,50,100,200}S = \{1, 2, 5, 10, 20, 50, 100, 200\} and n=200n = 200:

  • Time: Θ(kn)=Θ(8200)=Θ(1600)\Theta(kn) = \Theta(8 \cdot 200) = \Theta(1600) arithmetic operations.
  • Space: Θ(n)=Θ(201)\Theta(n) = \Theta(201) integers.

Proof. Follows directly from Theorem 2 with k=8k = 8 and n=200n = 200. Each arithmetic operation (addition, array access) is O(1)O(1) since all intermediate values fit in a machine word (f(8,200)=73682<231f(8, 200) = 73682 < 2^{31}). \square

Answer

73682\boxed{73682}

Code

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

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

int main() {
    const int target = 200;
    const int coins[] = {1, 2, 5, 10, 20, 50, 100, 200};
    int dp[target + 1] = {};
    dp[0] = 1;
    for (int c : coins)
        for (int j = c; j <= target; j++)
            dp[j] += dp[j - c];
    cout << dp[target] << endl;  // 73682
    return 0;
}