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...
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 be a finite set of positive integers (coin denominations). A representation of a non-negative integer over is a tuple satisfying . The change-making function counts the number of such representations.
Theorem 1 (Generating function). The change-making function satisfies
where denotes extraction of the coefficient of .
Proof. Each factor admits the geometric series expansion , valid for . The Cauchy product of such series yields
The coefficient of in this product equals the cardinality of , which is by Definition 1.
Lemma 1 (Recurrence). Define as the number of representations of using only denominations . Then:
Proof. The case admits exactly the empty sum. The case , has no coins available. For , the representations of over partition into two disjoint classes:
- Representations with : these are precisely the representations of over , counted by .
- Representations with : subtracting one copy of gives a bijection with representations of over , counted by .
These classes are exhaustive and mutually exclusive, establishing the recurrence. The case forces , collapsing to .
Theorem 2 (Space-optimal computation). The function can be computed in time and space using a one-dimensional array .
Proof. Initialize and for . This encodes for all .
Claim. After processing coin (for ) via the update for (in increasing order), we have for all .
Proof of claim by induction on . The base case holds by initialization. Suppose for all at the start of iteration . Consider the update for a given :
- Before is processed, (not yet updated in this iteration).
- Since and we process in increasing order, has already been updated to .
- After the update: by Lemma 1.
For , no update occurs, so (again by Lemma 1). This completes the inductive step.
After all iterations, . The outer loop runs times, the inner loop at most times each, giving time. The array has entries, giving space.
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 , 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 and :
- Time: arithmetic operations.
- Space: integers.
Proof. Follows directly from Theorem 2 with and . Each arithmetic operation (addition, array access) is since all intermediate values fit in a machine word ().
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#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;
}
"""Project Euler Problem 31: Coin Sums"""
def coin_sums(target, coins):
dp = [0] * (target + 1)
dp[0] = 1
for c in coins:
for j in range(c, target + 1):
dp[j] += dp[j - c]
return dp[target]
def main():
print(coin_sums(200, [1, 2, 5, 10, 20, 50, 100, 200])) # 73682
if __name__ == "__main__":
main()