DSA sheet / Dynamic programming

Removing Digits

easy about 15 min dpgreedy-vs-dpshortest-path
Open on CSES

Do these first: Minimizing Coins

The problem in brief

Starting from an integer n (up to a million), each step subtracts one digit that appears in the current number from that number. Print the minimum number of steps needed to reach 0.

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

First test the tempting greedy (always subtract the largest digit). Greedy often works here but you should not rely on an unproved rule; a DP is simple enough that it does not need the risk. The transferable step is recognising a shortest path in a graph whose edges always point to smaller values: that is a DAG, so DP in increasing order of the value is a valid topological order.

State: steps[v] = fewest steps from v to 0. Transition: for each nonzero digit d of v, steps[v] = 1 + steps[v - d], minimised over all digits. Base: steps[0] = 0. A digit of 0 would produce a self-loop (v - 0 = v), which is useless and would break the recurrence, so skip it.

The habit: when a “minimum number of moves” problem has moves that strictly decrease some quantity, it is a DP over that quantity.

Intuition

Think of the numbers as floors in a building and each number as offering elevators that drop by exactly its own digits. You want the fewest elevator rides from floor n down to the ground. Work from the ground up: the answer for each floor uses answers you already computed for lower floors.

Approach
  1. Create steps[0..n] with steps[0] = 0.
  2. For v from 1 to n: extract the digits of v; for each nonzero digit d, take steps[v] = min(steps[v], steps[v - d] + 1).
  3. Print steps[n].

Pitfalls: skip zero digits; every v >= 1 has at least one nonzero digit, so steps[v] is always defined; digit extraction is a tiny inner loop (at most 7 digits for n = 10^6).

Complexity

O(n * digits) time, about 7 * 10^6 operations, and O(n) 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;
    scanf("%d", &n);
    vector<int> steps(n + 1, 0);
    for (int v = 1; v <= n; v++) {
        int best = INT_MAX;
        for (int t = v; t > 0; t /= 10) {
            int d = t % 10;
            if (d > 0) best = min(best, steps[v - d] + 1);
        }
        steps[v] = best;
    }
    printf("%d\n", steps[n]);
    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