Smallest Number with N Digits and Digit Sum S is a clean greedy problem. It teaches a core greedy habit: figure out where a quantity does the least damage, and pour it there first.
Problem. Given two integers n (the number of digits) and s (the required digit sum), return the
smallest number, as a string, that has exactly n digits and whose digits add up to s. If no such
number exists, return "-1".
Example: n = 3, s = 20 → answer "299" (because 2 + 9 + 9 = 20, and no smaller 3-digit number has digit sum 20).
The slow way first
You could try every n-digit number in order and return the first whose digits sum to s. For n = 7 that is millions of candidates, and it only gets worse — this brute-force search is hopeless for large n.
The question to ask: to make a number small, where should the big digits go? A digit in a higher place value (further left) costs far more than the same digit on the right. So to keep the number small, we want the left digits tiny and the right digits large.
The idea: pour 9s in from the right
Walk the slots from the rightmost to the leftmost. At each slot, place as much sum as it can hold — min(9, remaining) — and subtract that from remaining. The big chunks land on the right where they cost the least, and whatever is left over trickles into the leading digit.
Two guard rails. If s < 1 there is no valid leading digit (a number cannot start with 0), and if s > 9 * n even all 9s cannot reach the sum — both cases are impossible, so we return "-1" up front. Otherwise the greedy fill always works, and the leading digit ends up at least 1.
Walk through it
Step through the animation. The pointer i starts at the rightmost slot. With s = 20: the first slot takes 9 (remaining 11), the next takes 9 (remaining 2), and the leading slot takes the remaining 2. The digits read 2, 9, 9 → "299".
Pseudocode
if s < 1 or s > 9 * n:
return "-1" # impossible
make a list of n zeros called digits
remaining = s
for i from the last slot down to the first:
digits[i] = min(9, remaining) # pour in as much as fits
remaining = remaining - digits[i]
return digits joined into a stringThe Python solution
def smallest_number(n, s):
if s < 1 or s > 9 * n:
return "-1"
digits = [0] * n
remaining = s
for i in range(n - 1, -1, -1):
digits[i] = min(9, remaining)
remaining -= digits[i]
return "".join(map(str, digits))- The first guard returns
"-1"when the sum is impossible: too small for a leading digit, or too big even for all 9s. digitsis the list of slots, left to right;remainingis how much sum is still unplaced.- The loop runs
ifromn - 1down to0, so we fill the rightmost slots first. - Line 7 is the greedy move: each slot grabs
min(9, remaining)— the most it can legally hold. - When the loop ends,
remainingis 0 and the leading digit holds the leftover, which is at least 1 becauses >= 1.
Complexity
| Case | Time | Notes |
|---|---|---|
| Brute force (try every number) | O(10^n) (moderate) | scans candidates in order |
| Greedy fill (this solution) | O(n) (moderate) | one digit per slot |
O(n) (moderate)We touch each of the n slots exactly once, so the work is linear in the number of digits. The only space is the digits list itself.
When this pattern shows up
When a problem asks for the smallest or largest thing built from parts, ask where each part hurts the least. Greedy often means: sort or order the positions by impact, then assign the extreme values (here, 9s) to the cheapest positions first.
Do not forget the feasibility check. With s = 0 the answer is not "000" — a number cannot have a
leading zero, so it is impossible and you return "-1". Likewise s > 9 * n can never be reached.
Practice
For n = 2 and s = 19, what does the greedy fill produce?
1. Why do we fill the digits starting from the right?
2. When is the answer impossible?
3. What value does each slot receive in the loop?
4. What is the time complexity of the greedy solution?