DSA sheet / Dynamic programming

Minimizing Coins

easy about 20 min dpcoin-changeunbounded-knapsack
Open on CSES

Do these first: Dice Combinations

The problem in brief

You have n coin values (n up to 100, each up to 10^6) and may use each value any number of times. Given a target x (up to 10^6), print the smallest number of coins that sum to exactly x, or -1 if it cannot be done.

Try it

Tab indents; press Esc, then Tab, to leave the box. Ctrl+Enter runs.

Hints

Stuck? Reveal one hint at a time. Each nudges without giving away the next.

The walkthrough

Spoilers ahead: open a section only after you have given the hints a fair try.

How to think

Coin change is the standard example where greedy is wrong and DP is right. You can defend that by finding a counterexample (coins 1, 3, 4 with target 6: greedy takes 4 + 1 + 1 = 3 coins, but 3 + 3 = 2 coins is better). Once greedy is out, use the recipe: state, transition, base case.

State: best[s], the fewest coins for total s. Transition: whichever coin c was used last, best[s] = 1 + best[s - c]; choose the minimum over all c <= s. Base: best[0] = 0. Unreachable totals get a big sentinel value (bigger than any real answer but small enough not to overflow when you add 1).

This is unbounded knapsack: the same item may be reused, which is why a single left-to-right pass over s (rather than a reversed pass) is correct.

Intuition

To pay s, decide which coin you hand over last. The rest has to be paid exactly, and you already know the cheapest way to pay every smaller amount. Try each coin and keep the cheapest total.

Approach
  1. best[0] = 0 and best[s] = INF for s from 1 to x, with INF such as 10^9.
  2. For s from 1 to x, for each coin c <= s: best[s] = min(best[s], best[s - c] + 1).
  3. Print best[x], or -1 if it is still INF.

Pitfalls: INF + 1 must not overflow (10^9 + 1 is fine in 32 bits); the number of coins needed is at most x itself. Work is n * x, up to 10^8, which is fine in C++ and Java. CPython is too slow at the maximum limits, so submit Python solutions as PyPy on judges that offer it.

Complexity

O(n * x) time, O(x) memory.

Solutions

Written from scratch and checked by compiling and running each one against a brute force on random inputs. Fast input/output, the way you would submit it.

Show solutions (C++17, Python 3, Java 17, Node.js)
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, x;
    scanf("%d %d", &n, &x);
    vector<int> coins(n);
    for (auto &c : coins) scanf("%d", &c);
    const int INF = 1000000000;
    vector<int> best(x + 1, INF);
    best[0] = 0;
    for (int s = 1; s <= x; s++) {
        for (int c : coins) {
            if (c <= s && best[s - c] + 1 < best[s]) best[s] = best[s - c] + 1;
        }
    }
    printf("%d\n", best[x] >= INF ? -1 : best[x]);
    return 0;
}

Problem: CSES Problem Set (Antti Laaksonen, University of Helsinki), CC BY-NC-SA 4.0. The explanations and code above are original (© Anupam Kumar).

Related problems

Back to the sheet