Sum of Numbers With Units Digit K
MedPrompt
Given two integers num and k, consider a set of positive integers with the following properties:
- The units digit of each integer is
k. - The sum of the integers is
num.
Return the minimum possible size of such a set, or -1 if no such set exists.
Note:
- The set can contain multiple instances of the same integer, and the sum of an empty set is considered
0. - The units digit of a number is the rightmost digit of the number.
Example 1:
Input: num = 58, k = 9
Output: 2
Explanation:
One valid set is [9,49], as the sum is 58 and each integer has a units digit of 9.
Another valid set is [19,39].
It can be shown that 2 is the minimum possible size of a valid set.Example 2:
Input: num = 37, k = 2
Output: -1
Explanation: It is not possible to obtain a sum of 37 using only integers that have a units digit of 2.Example 3:
Input: num = 0, k = 7
Output: 0
Explanation: The sum of an empty set is considered 0.
Constraints:
0 <= num <= 30000 <= k <= 9
Approaches
3 approaches with complexity analysis and trade-offs.
This approach attempts to solve the problem by exploring all possible combinations of numbers that end in k to see if they can sum up to num. A recursive function is defined to try subtracting every possible valid number (e.g., k, 10+k, 20+k, etc.) from the target num and finding the minimum count of numbers used in this process.
Algorithm
- Define a recursive function
solve(target)that returns the minimum number of integers ending inkthat sum totarget. - Base Case 1: If
target == 0, it means we have successfully formed the sum, so we need 0 more numbers. Return 0. - Base Case 2: If
target < 0, it's an invalid path. Return a value indicating impossibility, likeInteger.MAX_VALUE. - Recursive Step: Initialize a variable
minSizetoInteger.MAX_VALUE. - Iterate through all possible positive integers
cthat end with digitkand are less than or equal to the currenttarget(e.g.,k,10+k,20+k, ... or10,20, ... ifk=0). - For each
c, make a recursive callsolve(target - c). - If the recursive call does not return
Integer.MAX_VALUE, it means a solution was found for the subproblem. UpdateminSize = Math.min(minSize, 1 + solve(target - c)). - Return
minSize. - The initial call is
solve(num). If the result isInteger.MAX_VALUE, it means no solution exists, so return -1.
Walkthrough
The core idea is to use a recursive function, let's call it solve(target), which calculates the minimum number of items to reach a target sum. The function works as follows:
- If the
targetis 0, it means we've found a valid combination, and the size of this sub-solution is 0. We return 0. - If the
targetbecomes negative, it means the last number we subtracted was too large, so this path is invalid. We return an indicator of failure, such as infinity. - For a positive
target, we iterate through all valid numberscthat we can use (positive integers ending ink). For eachc, we recursively callsolve(target - c). We are looking for the minimum result among1 + solve(target - c)over all possible choices ofc.
This method is a classic brute-force search. It explores the entire search space, but because it recalculates solutions for the same target values multiple times, its performance is very poor.
Complexity
Time
Exponential - O(c^N) where N is `num` and c is the number of choices at each step. The function branches for every possible number ending in `k`, leading to an exponential number of calls.
Space
O(num) - The maximum depth of the recursion stack can be proportional to `num` in the worst case (e.g., if `k=1` and we keep subtracting 11, 21, etc.).
Trade-offs
Pros
Conceptually simple and a direct translation of the problem statement.
Cons
Extremely inefficient due to re-computation of the same subproblems.
Will result in a 'Time Limit Exceeded' (TLE) error for the given constraints.
This approach improves upon the brute-force recursion by using dynamic programming to avoid recomputing results for the same subproblems. It's analogous to the classic Unbounded Knapsack or Change-making problem. We build a dp array from the bottom up, where dp[i] stores the minimum number of integers ending in k that sum up to i.
Algorithm
- Create a
dparray of sizenum + 1, wheredp[i]will store the minimum size of a set that sums toi. - Initialize
dp[0] = 0(an empty set sums to 0) and all otherdp[i]to a value representing infinity (e.g.,Integer.MAX_VALUE). - Iterate with an outer loop from
i = 1tonum. - Inside this loop, have an inner loop that iterates through all possible positive numbers
cthat end inkand are less than or equal toi. - The numbers
cwill be of the formk, 10+k, 20+k, ...(ifk > 0) or10, 20, 30, ...(ifk = 0). - For each
c, if a solution fordp[i - c]exists (i.e., it's not infinity), updatedp[i]with the formula:dp[i] = Math.min(dp[i], 1 + dp[i - c]). - After the loops complete,
dp[num]holds the answer. Ifdp[num]is still infinity, no solution exists; return -1. Otherwise, returndp[num].
Walkthrough
We use a 1D array, dp, of size num + 1. The state dp[i] represents the minimum size of the set of numbers (each ending in k) that sums to i. Our goal is to compute dp[num].
We initialize dp[0] to 0, as an empty set has a sum of 0 and size 0. All other dp entries are initialized to infinity to indicate they are not yet reachable.
We then iterate from i = 1 to num. For each i, we try to form the sum i by taking a previously computed sum i - c and adding one more number c to it. The number c must be a positive integer ending in k. We check all such valid c's that are less than or equal to i. The dp[i] is then the minimum of 1 + dp[i - c] over all valid c's. This ensures that at each step i, we compute the optimal solution based on previously computed optimal solutions for smaller sums.
public int minimumNumbers(int num, int k) { if (num == 0) { return 0; } int[] dp = new int[num + 1]; Arrays.fill(dp, Integer.MAX_VALUE); dp[0] = 0; for (int i = 1; i <= num; i++) { if (k == 0) { for (int c = 10; c <= i; c += 10) { if (dp[i - c] != Integer.MAX_VALUE) { dp[i] = Math.min(dp[i], 1 + dp[i - c]); } } } else { for (int c = k; c <= i; c += 10) { if (dp[i - c] != Integer.MAX_VALUE) { dp[i] = Math.min(dp[i], 1 + dp[i - c]); } } } } return dp[num] == Integer.MAX_VALUE ? -1 : dp[num];}Complexity
Time
O(num^2) - The outer loop runs `num` times, and the inner loop can run up to `num / 10` times in the worst case, leading to a quadratic time complexity.
Space
O(num) - We need an array of size `num + 1` to store the DP states.
Trade-offs
Pros
Guarantees finding the optimal solution if one exists.
Significantly more efficient than brute-force, passing the given constraints.
Cons
Requires O(num) extra space for the DP table.
Slower than the optimal mathematical approach.
The most efficient solution leverages a key mathematical observation about the properties of the sum. The units digit of num is solely determined by the number of elements in the set (s) and the required units digit k. This allows us to drastically reduce the search space and find the answer with a simple loop.
Algorithm
- Handle the edge case: if
num == 0, the size of the set is 0. Return 0. - Iterate through possible set sizes
sfrom 1 to 10. - For each size
s, check two necessary conditions:- Sum Condition: The sum of
spositive numbers ending inkmust be at leasts * k. Therefore, we must haves * k <= num. - Units Digit Condition: The units digit of the sum of
snumbers ending inkis determined by(s * k) % 10. This must match the units digit ofnum. So, we check if(s * k) % 10 == num % 10.
- Sum Condition: The sum of
- If both conditions are met for a size
s, we have found a potential solution. Since we are iteratingsfrom 1 upwards, the firststhat satisfies both conditions is the minimum possible size. Returnsimmediately. - If the loop finishes (i.e., we check all sizes from 1 to 10) and no such
sis found, it's impossible to form the sumnum. Return -1.
Walkthrough
Let the size of the set be s. The sum of s numbers, each with a units digit of k, must result in a number whose units digit matches num's units digit. The sum of the units digits of the s numbers is s * k. Therefore, the units digit of the total sum num must be equal to the units digit of s * k. This gives us the condition: (s * k) % 10 == num % 10.
Furthermore, since the numbers in the set must be positive, the smallest possible number ending in k is k itself (if k > 0) or 10 (if k = 0). Thus, the smallest possible sum for a set of size s is s * k (or s * 10 if k=0). This sum cannot exceed num, leading to the condition s * k <= num.
The crucial insight is that we only need to check for s from 1 to 10. The sequence of units digits (s * k) % 10 is periodic and repeats every 10 values of s. If a solution exists with a size s' > 10, then a smaller solution s = s' % 10 (or s=10 if s'%10==0) would also satisfy the units digit condition while having a smaller required sum s*k, making it a better candidate. Therefore, if any solution exists, the minimum size must be within the range [1, 10].
This reduces the problem to a constant number of checks.
public int minimumNumbers(int num, int k) { if (num == 0) { return 0; } for (int s = 1; s <= 10; s++) { // Check if a set of size 's' is possible. // 1. The sum must be large enough: s * k <= num // 2. The units digit must match: (s * k) % 10 == num % 10 if (s * k <= num && (s * k) % 10 == num % 10) { return s; } } return -1;}Complexity
Time
O(1) - The loop runs a constant number of times (at most 10), regardless of the value of `num` or `k`.
Space
O(1) - No extra space is used that depends on the size of the input.
Trade-offs
Pros
Extremely efficient with constant time and space complexity.
Simple and concise implementation.
Cons
The logic relies on a mathematical property that may not be immediately obvious.
Solutions
Solution
class Solution {public int minimumNumbers(int num, int k) { if (num == 0) { return 0; } for (int i = 1; i <= num; ++i) { int t = num - k * i; if (t >= 0 && t % 10 == 0) { return i; } } return -1; }}class Solution {public: int minimumNumbers(int num, int k) { if (num == 0) return 0; for (int i = 1; i <= num; ++i) { int t = num - k * i; if (t >= 0 && t % 10 == 0) return i; } return -1; }};class Solution: def minimumNumbers(self, num: int, k: int) -> int: if num == 0: return 0 for i in range(1, num + 1): if (t: = num - k * i) >= 0 and t % 10 == 0: return i return - 1Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.