1283. Find the Smallest Divisor Given a Threshold
Problem Description
You are given an array of integers nums and an integer threshold. Your task is to find the smallest positive integer divisor such that when you divide all elements in the array by this divisor and sum up the results, the total sum is less than or equal to the threshold.
When dividing each element, the result must be rounded up to the nearest integer (ceiling function). For example:
7/3 = 3(since 2.33... rounds up to 3)10/2 = 5(exactly 5, no rounding needed)
The problem guarantees that there will always be a valid answer.
Example walkthrough:
If nums = [1, 2, 5, 9] and threshold = 6:
- If divisor = 5:
ceil(1/5) + ceil(2/5) + ceil(5/5) + ceil(9/5) = 1 + 1 + 1 + 2 = 5 ≤ 6✓ - If divisor = 4:
ceil(1/4) + ceil(2/4) + ceil(5/4) + ceil(9/4) = 1 + 1 + 2 + 3 = 7 > 6✗
The goal is to find the smallest divisor value that keeps the sum within the threshold limit.
Intuition
The key observation is that as the divisor increases, the sum of divided results decreases. This creates a monotonic relationship:
- Small divisor → Large sum (each division gives a larger result)
- Large divisor → Small sum (each division gives a smaller result)
For example, with nums = [10]:
- divisor = 1:
ceil(10/1) = 10 - divisor = 2:
ceil(10/2) = 5 - divisor = 5:
ceil(10/5) = 2 - divisor = 10:
ceil(10/10) = 1
Since we want the smallest divisor where the sum ≤ threshold, we're looking for a turning point: the first value where the condition becomes true. Everything to the left fails (sum too large), and everything to the right succeeds (sum small enough).
This monotonic property screams binary search! We can search for the smallest valid divisor in the range [1, max(nums)]. Why max(nums) as the upper bound? Because dividing any number by a value greater than or equal to itself always yields 1 (after ceiling), so using any divisor larger than max(nums) won't reduce the sum further.
The binary search checks each candidate divisor by calculating the sum. If the sum is within the threshold, we know this divisor works, but there might be a smaller one, so we search the left half. If the sum exceeds the threshold, we need a larger divisor to reduce the sum, so we search the right half.
Learn more about Binary Search patterns.
Solution Implementation
1class Solution:
2 def smallestDivisor(self, nums: List[int], threshold: int) -> int:
3 """
4 Find the smallest divisor such that sum of ceil(nums[i]/divisor) <= threshold.
5
6 Uses binary search to find the first divisor where the condition is satisfied.
7 The feasible function returns True when sum <= threshold.
8 Pattern: false, false, ..., true, true, true (find first true)
9 """
10 def feasible(divisor: int) -> bool:
11 """Check if this divisor produces a sum <= threshold."""
12 total = sum((num + divisor - 1) // divisor for num in nums)
13 return total <= threshold
14
15 # Binary search range: [1, max(nums)]
16 left, right = 1, max(nums)
17 first_true_index = -1
18
19 while left <= right:
20 mid = (left + right) // 2
21 if feasible(mid):
22 first_true_index = mid
23 right = mid - 1 # Look for smaller valid divisor
24 else:
25 left = mid + 1
26
27 return first_true_index
281class Solution {
2 /**
3 * Find the smallest divisor such that sum of ceil(nums[i]/divisor) <= threshold.
4 * Uses binary search template to find first true index.
5 * Pattern: false, false, ..., true, true, true (find first true)
6 */
7 public int smallestDivisor(int[] nums, int threshold) {
8 // Find max element for upper bound
9 int maxNum = 0;
10 for (int num : nums) {
11 maxNum = Math.max(maxNum, num);
12 }
13
14 // Binary search range: [1, maxNum]
15 int left = 1;
16 int right = maxNum;
17 int firstTrueIndex = -1;
18
19 while (left <= right) {
20 int mid = left + (right - left) / 2;
21
22 // Check if mid is feasible (sum <= threshold)
23 int sum = 0;
24 for (int num : nums) {
25 sum += (num + mid - 1) / mid; // Ceiling division
26 }
27
28 if (sum <= threshold) {
29 firstTrueIndex = mid;
30 right = mid - 1; // Look for smaller valid divisor
31 } else {
32 left = mid + 1;
33 }
34 }
35
36 return firstTrueIndex;
37 }
38}
391class Solution {
2public:
3 /**
4 * Find the smallest divisor such that sum of ceil(nums[i]/divisor) <= threshold.
5 * Uses binary search template to find first true index.
6 * Pattern: false, false, ..., true, true, true (find first true)
7 */
8 int smallestDivisor(vector<int>& nums, int threshold) {
9 // Binary search range: [1, max(nums)]
10 int left = 1;
11 int right = *max_element(nums.begin(), nums.end());
12 int firstTrueIndex = -1;
13
14 while (left <= right) {
15 int mid = left + (right - left) / 2;
16
17 // Check if mid is feasible (sum <= threshold)
18 int sum = 0;
19 for (int num : nums) {
20 sum += (num + mid - 1) / mid; // Ceiling division
21 }
22
23 if (sum <= threshold) {
24 firstTrueIndex = mid;
25 right = mid - 1; // Look for smaller valid divisor
26 } else {
27 left = mid + 1;
28 }
29 }
30
31 return firstTrueIndex;
32 }
33};
341/**
2 * Find the smallest divisor such that sum of ceil(nums[i]/divisor) <= threshold.
3 * Uses binary search template to find first true index.
4 * Pattern: false, false, ..., true, true, true (find first true)
5 *
6 * @param nums - Array of positive integers to divide
7 * @param threshold - Maximum allowed sum after division
8 * @returns The smallest positive integer divisor
9 */
10function smallestDivisor(nums: number[], threshold: number): number {
11 // Helper function to check if divisor is feasible
12 const feasible = (divisor: number): boolean => {
13 const sum = nums.reduce(
14 (acc, num) => acc + Math.ceil(num / divisor),
15 0
16 );
17 return sum <= threshold;
18 };
19
20 // Binary search range: [1, max(nums)]
21 let left = 1;
22 let right = Math.max(...nums);
23 let firstTrueIndex = -1;
24
25 while (left <= right) {
26 const mid = Math.floor((left + right) / 2);
27
28 if (feasible(mid)) {
29 firstTrueIndex = mid;
30 right = mid - 1; // Look for smaller valid divisor
31 } else {
32 left = mid + 1;
33 }
34 }
35
36 return firstTrueIndex;
37}
38Solution Approach
The solution uses binary search to find the smallest valid divisor. Here's how the implementation works:
Binary Search Setup:
- Left boundary:
l = 1(smallest possible divisor) - Right boundary:
r = max(nums)(any divisor larger than this won't reduce the sum further)
The Search Process:
- Calculate the middle point:
mid = (l + r) // 2 - For the current
mid, calculate the sum of all divisions:s = sum(ceil(x/mid) for x in nums) - Compare the sum with threshold:
- If
s ≤ threshold: This divisor works, but there might be a smaller one. Updater = mid - If
s > threshold: We need a larger divisor to reduce the sum. Updatel = mid + 1
- If
- Repeat until
lmeetsr
Calculating Ceiling Division:
The code uses a clever trick to calculate ceil(x/v) without using the ceiling function:
- Formula:
(x + v - 1) // v - This works because adding
v - 1to the numerator ensures rounding up for any remainder
The Given Solution's Approach:
The provided code uses Python's bisect_left with a custom key function:
def f(v: int) -> bool:
v += 1
return sum((x + v - 1) // v for x in nums) <= threshold
return bisect_left(range(max(nums)), True, key=f) + 1
This function f(v) returns True if divisor v+1 produces a sum ≤ threshold. The bisect_left finds the leftmost position where True would fit in the boolean sequence, which corresponds to our smallest valid divisor. The +1 adjustments handle the 0-indexed nature of the range.
Time Complexity: O(n * log(max(nums))) where n is the length of the array
Space Complexity: O(1) excluding the input array
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the binary search approach.
Given: nums = [2, 7, 11] and threshold = 10
Goal: Find the smallest divisor such that ceil(2/d) + ceil(7/d) + ceil(11/d) ≤ 10
Step 1: Initialize binary search boundaries
- Left boundary:
l = 1 - Right boundary:
r = max(nums) = 11 - Search range:
[1, 11]
Step 2: First iteration
mid = (1 + 11) // 2 = 6- Calculate sum with divisor 6:
ceil(2/6) + ceil(7/6) + ceil(11/6) = 1 + 2 + 2 = 5
- Since
5 ≤ 10, divisor 6 works! But can we find a smaller one? - Update:
r = 6, new range:[1, 6]
Step 3: Second iteration
mid = (1 + 6) // 2 = 3- Calculate sum with divisor 3:
ceil(2/3) + ceil(7/3) + ceil(11/3) = 1 + 3 + 4 = 8
- Since
8 ≤ 10, divisor 3 works! Continue searching for smaller. - Update:
r = 3, new range:[1, 3]
Step 4: Third iteration
mid = (1 + 3) // 2 = 2- Calculate sum with divisor 2:
ceil(2/2) + ceil(7/2) + ceil(11/2) = 1 + 4 + 6 = 11
- Since
11 > 10, divisor 2 is too small (sum exceeds threshold). - Update:
l = 3, new range:[3, 3]
Step 5: Convergence
l = r = 3, so we've found our answer!
Answer: The smallest divisor is 3, which gives us a sum of 8 ≤ 10.
Verification of neighboring values:
- Divisor 2: sum = 11 (too large, exceeds threshold)
- Divisor 3: sum = 8 (valid, within threshold) ✓
- Divisor 4: sum = 6 (valid, but not the smallest)
The binary search efficiently found the turning point where the divisor becomes just large enough to keep the sum within the threshold.
Time and Space Complexity
The time complexity is O(n × log M), where n is the length of the array nums and M is the maximum value in the array nums. This is because:
- The binary search using
bisect_leftperformsO(log M)iterations over the range[0, max(nums)). - In each iteration, the function
fis called, which computes the sum of ceiling divisions for all elements innums, takingO(n)time. - Therefore, the total time complexity is
O(n × log M).
The space complexity is O(1). The algorithm only uses a constant amount of extra space for variables like v and the sum calculation, regardless of the input size. The range(max(nums)) in bisect_left is treated as a virtual range and doesn't create an actual list in memory.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template Variant
The most critical mistake is using while left < right with right = mid instead of the standard template. This problem requires finding the first true index (smallest valid divisor).
Wrong template:
while left < right: mid = (left + right) // 2 if feasible(mid): right = mid # Wrong pattern else: left = mid + 1 return left
Correct template:
first_true_index = -1 while left <= right: mid = (left + right) // 2 if feasible(mid): first_true_index = mid right = mid - 1 # Correct: search left for smaller else: left = mid + 1 return first_true_index
2. Incorrect Ceiling Division Formula
Many developers try to use floating-point division with math.ceil, which can lead to precision issues.
Problematic approach:
import math
total_sum = sum(math.ceil(num / divisor) for num in nums)
# Can have floating-point precision issues
Better approach:
# Integer-only arithmetic: ceil(x/d) = (x + d - 1) // d
total_sum = sum((num + divisor - 1) // divisor for num in nums)
3. Wrong Binary Search Boundaries
Setting the right boundary incorrectly can miss valid solutions or cause unnecessary iterations.
Wrong:
left, right = 1, min(nums) # Too small - may miss valid divisors
left, right = 1, sum(nums) # Too large - wastes iterations
Correct:
# Right boundary should be max(nums) since any divisor larger
# than the maximum element won't reduce the sum further
left, right = 1, max(nums)
4. Not Tracking the Answer Separately
Returning left directly instead of tracking first_true_index can cause issues in edge cases.
Wrong:
while left <= right: mid = (left + right) // 2 if feasible(mid): right = mid - 1 else: left = mid + 1 return left # May be incorrect if no valid answer exists
Correct:
first_true_index = -1 while left <= right: mid = (left + right) // 2 if feasible(mid): first_true_index = mid # Track the answer right = mid - 1 else: left = mid + 1 return first_true_index
Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?
Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Want a Structured Path to Master System Design Too? Don’t Miss This!