Topics › Binary Search › BS on Answers
Find the smallest divisor
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given an array nums of positive integers and an integer threshold. Choose a positive integer divisor d, divide every element by d, round each quotient up to the nearest integer, and add the results. Return the smallest d for which that sum is less than or equal to threshold. The usual version of the problem guarantees that threshold ≥ n, so an answer always exists; our implementation returns -1 if it does not.
Constraints: 1 ≤ n ≤ 5 * 10^4, 1 ≤ nums[i] ≤ 10^6, n ≤ threshold ≤ 10^6.
Example 1
nums = [1, 2, 5, 9], threshold = 6
answer = 5 // ceil(1/5) + ceil(2/5) + ceil(5/5) + ceil(9/5) = 1 + 1 + 1 + 2 = 5 <= 6; with d = 4 the sum is 7
Example 2
nums = [44, 22, 33, 11, 1], threshold = 5
answer = 44 // threshold equals n, so every term must be exactly 1, which forces d >= max
If this looks like Koko eating bananas with different nouns, that is because it is: the piles are nums, the speed is d, and the hour limit is threshold.
The sum for a fixed divisor
static long sumWithDivisor(int[] nums, int d) {
long sum = 0;
for (int x : nums) sum += ((long) x + d - 1) / d;
return sum;
}
(x + d - 1) / d is integer ceil(x / d) for positive operands. The accumulator is a long: with 5 * 10^4 elements of 10^6 and d = 1 the sum is 5 * 10^10, which overflows int. The cast on x also protects x + d - 1 when both are large.
Approach 1: Brute force, try every divisor
Intuition. Any d ≥ max(nums) makes every term ceil(x / d) = 1, so the sum is exactly n, the smallest it can ever be. Divisors above max therefore never help, and the candidates are 1, 2, ..., max. Larger divisors give smaller (or equal) sums, so scanning from 1 upwards and stopping at the first divisor that satisfies the threshold yields the minimum.
Algorithm.
- Compute
max = max(nums). - For
d = 1, 2, ..., max: ifsumWithDivisor(nums, d) ≤ threshold, returnd. - Return
-1(only whenthreshold < n).
static int bruteForce(int[] nums, int threshold) {
int max = 0;
for (int x : nums) max = Math.max(max, x);
for (int d = 1; d <= max; d++) {
if (sumWithDivisor(nums, d) <= threshold) return d;
}
return -1;
}
Complexity. Time O(n * max): up to 10^6 divisors, each needing an O(n) pass, around 5 * 10^10 operations in the worst case. Space O(1).
Approach 2: Optimal, binary search on the divisor
Intuition. Let ok(d) be “the rounded-up sum for divisor d is at most threshold”. Increasing d can only decrease each ceil(x / d), so the sum is non-increasing in d, and ok is monotonic: false for small divisors, true from some point on. The answer is the first true value. Binary search over [1, max]: when ok(mid) holds, record mid and try smaller divisors; otherwise try larger ones.
Algorithm.
- Compute
max = max(nums); setlo = 1,hi = max,ans = -1. - While
lo ≤ hi:mid = lo + (hi - lo) / 2.- If
sumWithDivisor(nums, mid) ≤ threshold:ans = mid,hi = mid - 1. - Else:
lo = mid + 1.
- Return
ans.
static int optimal(int[] nums, int threshold) {
int max = 0;
for (int x : nums) max = Math.max(max, x);
int lo = 1, hi = max, ans = -1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (sumWithDivisor(nums, mid) <= threshold) { ans = mid; hi = mid - 1; }
else lo = mid + 1;
}
return ans;
}
Dry run on nums = [1, 2, 5, 9], threshold = 6:
| lo | hi | mid | terms | sum | ≤ 6? | ans | next |
|---|---|---|---|---|---|---|---|
| 1 | 9 | 5 | 1, 1, 1, 2 | 5 | yes | 5 | [1, 4] |
| 1 | 4 | 2 | 1, 1, 3, 5 | 10 | no | 5 | [3, 4] |
| 3 | 4 | 3 | 1, 1, 2, 3 | 7 | no | 5 | [4, 4] |
| 4 | 4 | 4 | 1, 1, 2, 3 | 7 | no | 5 | [5, 4] stop |
Result 5.
Complexity. Time O(n log max): about 20 iterations for max = 10^6, each an O(n) scan, so roughly 10^6 operations. Space O(1).
Here ans starts at -1 rather than at max, because the problem as implemented does not assume threshold ≥ n. When the guarantee holds, ok(max) is true and the search always records a value; when it does not, no divisor works and -1 is the honest answer.
Why the monotonic argument is airtight
It is tempting to wave hands at “bigger divisor, smaller sum”, so here is the precise statement. For positive integers x and d < d', x / d' < x / d as real numbers, hence ceil(x / d') ≤ ceil(x / d). Summing over all elements gives sum(d') ≤ sum(d). Therefore if sum(d) ≤ threshold then sum(d') ≤ threshold too: feasibility of d implies feasibility of every larger divisor, which is exactly the F...F T...T shape binary search needs. Note the inequality on each term is not strict (ceil(5/4) = ceil(5/5) = 1), which is why the sum can stay flat for a while, as rows three and four of the dry run show. Flat stretches are fine; the search only needs “never increases”.
Edge cases and pitfalls
threshold == n. Every term must be1, so the answer ismax(nums). The binary search gets there because everymid < maxleaves at least one term equal to2.threshold < n. Impossible; each term is at least1. Both methods return-1. The LeetCode version excludes this input.- Huge threshold. If
d = 1already works, the answer is1. The search must includelo = 1. - Overflow of the sum. Use
long. The test with three elements of10^6andthreshold = 3is small, but5 * 10^4elements of10^6withd = 1is not. - Rounding. The problem says round up.
x / din Java rounds down;(x + d - 1) / drounds up. Using the wrong direction gives a different answer for most inputs. d = 0. Division by zero. The range starts at1.- Searching above
max. Harmless but wasteful; divisors abovemaxall produce the same sumn.
Related problems
- Koko eating bananas is literally the same problem with piles instead of numbers and hours instead of a threshold.
- Capacity to Ship Packages Within D Days minimises a capacity using a greedy feasibility scan rather than an arithmetic one.
- Minimum days to make M bouquets searches over days instead of divisors.
- Find square root of a number is the simplest instance of the same
lo / hi / ansskeleton.