Binary Search on Answer — When Monotonicity Breaks
Non-monotonic feasibility returned 45s instead of 80s, overheating servers.
- Binary search on answer searches the solution space, not an array.
- Feasibility function must be monotonic: condition(X) guarantees condition(X+1).
- Common use: minimum speed, minimum days, smallest k satisfying constraints.
- Complexity: O(n * log(range)) where range is the answer space size.
- Biggest trap: defining a non-monotonic condition function.
Standard binary search finds a target in a sorted array in O(log n). But what if the problem is asking for a minimum or maximum value that satisfies some condition, and you can check any candidate in O(n)? That's binary search on answer. It turns a 'find the optimum' problem into a 'does this candidate work?' problem, as long as feasibility is monotone. You never store the array – you just evaluate condition(mid) for any mid in the implicit range. This is the technique behind problems like 'minimum eating speed', 'smallest divisor', and 'minimum days to make bouquets'. It's not a trick; it's a direct application of binary search logic to the answer domain.
What is Binary Search on Answer? — Plain English
Binary search on the answer (parametric search) solves optimization problems of the form 'find the minimum/maximum value X such that some condition is satisfied.' Instead of searching for X in an array, you binary search on the range of possible answers. The key requirement is monotonicity: if condition(X) is true, then condition(X+1) is also true (or false — the condition must be monotone). For example: 'what is the minimum number of days to make at least m bouquets?' If it is possible in d days, it is also possible in d+1 days (more time = more options). So binary search on the number of days, checking feasibility for each candidate.
How Binary Search on Answer Works — Step by Step
Template for 'find minimum X where condition(X) is true':
- Determine the search range: lo = minimum possible answer, hi = maximum possible answer.
- While lo < hi:
- a. mid = (lo + hi) // 2.
- b. If condition(mid) is True: hi = mid (answer might be mid or smaller).
- c. If condition(mid) is False: lo = mid + 1 (need a larger answer).
- Return lo (the smallest X where condition is True).
- If condition(mid) True: lo = mid + 1 (can try larger).
- If condition(mid) False: hi = mid
- 1 (too large).
- Return hi or lo-1 depending on the problem.
The condition function runs in O(n) or O(n log n), and binary search does O(log(answer_range)) iterations, giving total complexity O(n log(answer_range)).
When to Use Binary Search on Answer — A 4-Point Identification Checklist
Not every optimization problem is suitable for binary search on answer. Use this quick 4-point checklist before deciding:
- Optimization over a numeric range: The problem asks for a minimum or maximum value (e.g., minimum speed, maximum possible profit). If the answer is a value in a contiguous range, binary search on answer is a candidate.
- You can write a feasibility function: You must be able to answer 'can we achieve X?' for any candidate X, in polynomial time. This function converts the decision into a yes/no answer. If you cannot efficiently check feasibility, the technique won't work.
- Monotonic feasibility: The feasibility function must be monotonic — meaning as X increases, the outcome either stays true or switches from false to true exactly once (or the reverse for max-search). Test this with a few points before committing. If the function oscillates, binary search on answer will fail.
- O(log range) iterations are worth it: The feasibility function should be efficient enough that calling it ~log(range) times is acceptable. For example, if the range is 10^9 and feasibility is O(n), the total O(n * log(10^9)) is often fine. If feasibility is O(n^2), consider a different approach.
If all four points check out, binary search on answer is likely the right technique. If any are missing, look for alternative methods.
Worked Example — Koko Eating Bananas
Piles=[3,6,7,11], h=8 hours. Minimum eating speed k such that Koko can eat all piles within h hours?
For speed k, time per pile = ceil(pile/k). Total time = sum(ceil(pile/k) for all piles).
Search range: lo=1, hi=max(piles)=11.
mid=6: ceil(3/6)+ceil(6/6)+ceil(7/6)+ceil(11/6) = 1+1+2+2=6 <= 8. Condition True → hi=6. mid=3: ceil(3/3)+ceil(6/3)+ceil(7/3)+ceil(11/3) = 1+2+3+4=10 > 8. False → lo=4. mid=5: 1+2+2+3=8 <= 8. True → hi=5. mid=4: 1+2+2+3=8 <= 8. True → hi=4. lo==hi==4. Answer: k=4.
Verification: speed=4: ceil(3/4)=1, ceil(6/4)=2, ceil(7/4)=2, ceil(11/4)=3. Total=8 hours. Correct.
(pile + speed - 1) // speed.Floating-Point Binary Search on Answer for Continuous Optimization
Binary search on answer also works for continuous (real-number) answer spaces, as long as the feasibility function is monotonic. Instead of while lo < hi, you use while hi - lo > epsilon where epsilon is the desired precision. The feasibility function must also handle floating point comparisons with a tolerance.
Example: Find the square root of a number (without using math.sqrt). The feasibility function: (mid * mid <= x). Since square root is monotonic, binary search works.
Example: Find the minimum radius of a circle to cover all points. Feasibility checks if a circle of radius r covers all points. Since larger radius always works, monotonicity holds.
Pitfall: Floating point precision can cause infinite loops if epsilon is too small or the feasibility function is not robust. Use a maximum iteration limit (e.g., 100 iterations) as a safety net.
Template: ``python def float_binary_search(lo, hi, epsilon=1e-7): while hi - lo > epsilon: mid = (lo + hi) / 2 if feasible(mid): hi = mid # or lo = mid for max-search else: lo = mid # or hi = mid for max-search return (lo + hi) / 2 `` The feasibility function must be monotonic over the continuous range. Test with boundary values.
Implementation
Binary search on answer uses the standard binary search template but the array being searched is the implicit range [lo, hi] of possible answers. The feasibility check (condition function) encapsulates the domain-specific logic. Keep the template consistent: for minimum answer, use hi=mid when condition is True; for maximum answer, use lo=mid when condition is True. Always verify the boundary conditions with small examples.
- The boundary may be sharp or fuzzy — but must be a single switch point.
- The feasibility function is your oracle; it's the only way to decide which side of the boundary a candidate lies on.
- The search range must contain the boundary. If lo and hi are both feasible, binary search returns lo, not the true minimum.
C++ Implementation of Binary Search on Answer
The same algorithm works in C++ with careful integer division and type handling. The ceil division idiom (pile + speed - 1) / speed works because integer division truncates toward zero. For large ranges, prefer mid = lo + (hi - lo) / 2 to avoid overflow. The condition function is typically a lambda or separate function. Below is a C++ version of the Koko Eating Bananas problem and the Minimum Days to Make Bouquets problem.
lo + hi when the range exceeds 2^31. Using lo + (hi - lo) / 2 is a safe habit. Also, note that the 'ceil division' idiom works correctly for positive integers. For negative values, adjust accordingly, though answer spaces are usually non-negative.mid = lo + (hi - lo) / 2 to avoid overflow. The ceil division (x + d - 1) / d is the standard pattern. Test with edge cases like speed=1 and large piles.Common Pitfalls and How to Avoid Them
Binary search on answer is elegant but fragile. The #1 mistake: assuming monotonicity without verifying. #2 mistake: off-by-one errors in the search loop, especially in max-search. #3 mistake: using floating point division instead of integer ceil division. Always test with the smallest and largest possible answers. A safe habit: after the loop, assert that condition(lo) is True and condition(lo-1) is False (if lo > min range). For max-search, assert condition(hi) is True and condition(hi+1) is False.
mid = lo + (hi - lo) // 2 instead. In Python it's safe, but in other environments it's a classic bug.Off-by-One Boundary Pitfalls in Binary Search on Answer
Off-by-one errors are the most common implementation bugs in binary search on answer. They manifest as infinite loops or wrong answers by one unit. Here are two classic examples:
Example 1: Min-search with wrong lo update Wrong: lo = mid when condition is false (instead of lo = mid + 1). This can cause an infinite loop because mid might equal lo when the range shrinks to 1. For example, if lo=5, hi=6, mid=5, condition false, setting lo=5 never changes, infinite loop. Fix: always use lo = mid + 1 on false for min-search.
Example 2: Max-search with wrong hi update Wrong: hi = mid when condition is false (instead of hi = mid - 1). This can also cause an infinite loop or return a value that is too large. For max-search, the correct approach is: if condition(mid) true, set lo = mid; else set hi = mid - 1. The midpoint calculation must use mid = (lo + hi + 1) / 2 to avoid getting stuck when lo and hi are adjacent.
Fix: Always test your algorithm with a small manual example where you know the answer. Use assertions after the loop to check that condition(lo) is True (for min-search) and condition(lo-1) is False.
lo = mid on false. The search converged to a value that was one unit too small, causing jobs to be rejected. The fix was a code review that caught the off-by-one pattern. We now have a unit test that checks the boundary conditions explicitly.Classic Binary Search on Answer Problems (LeetCode)
The following LeetCode problems are excellent practice for mastering binary search on answer. Each involves a monotonic feasibility function and a numeric answer space.
1. LeetCode 875 – Koko Eating Bananas Problem: Koko can eat at speed k bananas per hour. Given piles, find minimum k to eat all in h hours. Approach: Binary search speed from 1 to max(piles). Feasibility: sum(ceil(pile/k)) <= h. Monotonic because faster speed always reduces total time.
2. LeetCode 1011 – Capacity To Ship Packages Within D Days Problem: Ship packages in order within D days. Find minimum ship capacity. Approach: Binary search capacity from max(weights) to sum(weights). Feasibility: simulate loading days, ensure days <= D. Monotonic because larger capacity never increases days.
3. LeetCode 1482 – Minimum Number of Days to Make m Bouquets Problem: Given bloomDay array, make m bouquets of k adjacent flowers. Find minimum days. Approach: Binary search days from min(bloomDay) to max(bloomDay). Feasibility: simulate blooming and count adjacent blooms. Monotonic because more days can only increase bouquets.
4. LeetCode 1283 – Find the Smallest Divisor Given a Threshold Problem: Find smallest divisor such that sum of ceil(nums[i]/div) <= threshold. Approach: Binary search divisor from 1 to max(nums). Feasibility: sum ceil division. Monotonic because larger divisor always reduces sum.
5. LeetCode 774 – Minimize Max Distance to Gas Station (premium, floating-point variant) Problem: Add k gas stations to minimize maximum distance between adjacent stations. Approach: Binary search on the maximum gap. Feasibility: count how many stations needed to ensure all gaps <= candidate. Monotonic because larger gaps require fewer stations.
Wrong Minimum Because Feasibility Wasn't Monotonic
- Never assume monotonicity without proof. Plot feasibility for a handful of candidate values before trusting binary search.
- Binary search on answer is correct only if condition(X) is monotonic in the answer space. A single counterexample breaks it.
Key takeaways
Common mistakes to avoid
4 patternsAssuming monotonicity without validation
Using wrong mid calculation for max-search
(lo+hi+1)//2.mid = (lo + hi + 1) // 2. If condition(mid) is True, set lo = mid; else set hi = mid - 1.Off-by-one in search range bounds
Feasibility function uses floating point division
(x + d - 1) // d. If floating point is unavoidable, add a small epsilon and use math.isclose.Interview Questions on This Topic
How do you identify a problem that can be solved with binary search on answer?
Frequently Asked Questions
That's Searching. Mark it forged?
7 min read · try the examples if you haven't