DSA sheet · Binary Search · Binary search on answer (min of max)
Allocate Minimum Pages
Books sit in a row, and we must hand them out to k students in continuous blocks. The student with the most pages has the hardest job, and we want that hardest job to be as light as possible. This is the "minimise the maximum" version of binary search on the answer, the mirror image of Aggressive Cows. The teacher first lists every possible split by hand to understand the question, then picks the answer range (max book → total pages), writes a "how many students do I need?" check, runs the brute force to show the TLE, and finally binary searches over the range.
Why it matters: this problem is the parent of a whole family (Split Array Largest Sum, Painter's Partition, Ship Packages). Split Array Largest Sum is literally the same code.
Every part below follows the same order:
① the question in simple words → ② what the constraints tell us → ③ intuition → ④ building the logic from examples → ⑤ approach steps → ⑥ code → ⑦ code line by line → ⑧ dry run → ⑨ complexity & remember
- Part 0 · What you must know before starting
- Part A · Brute force: try every page limit one by one
- Part B · Optimal: binary search on the page limit
- Part C · Revision page
Part 0 · Before starting
Normal binary search in one paragraph
Binary search works when a yes/no question changes its answer only once along a sorted line. Keep low and high, test the middle mid, and throw away the half that can't hold the answer. Each step halves the range, so about log₂(size) steps are needed.
How to compute mid, and why that form
mid = low + (high - low) // 2
The value is the same as (low + high) // 2. In Java or C++, adding two big ints can go past the int limit (about 2.1 × 10⁹) and give a wrong negative number. high - low never overflows. The teacher says she uses this form whenever the constraints are big. Here high is a sum of pages, which can reach 10⁶ × 10³ = 10⁹. Python integers never overflow, so in Python it's only a good habit.
The pattern: binary search on the answer
- Answer range: the smallest possible answer (
low) and the largest (high). - Yes/no check: test one candidate. Here: "if no student may read more than X pages, can k students cover all the books?"
- Monotonic: all the noes on one side, all the yeses on the other. Then binary search finds the boundary.
The teacher's tell-tale sign: in the example the answer is 113, and 113 is not in the array. The answer doesn't come from the input array, it comes from a range of numbers. That's why we binary search a range and not the array. (The example array happens to be sorted, but that doesn't help us. She asks whether to binary search the array itself and says no.)
Minimise the maximum vs maximise the minimum
| minimise the maximum (this page, Split Array) | maximise the minimum (Aggressive Cows, Magnetic Force) | |
|---|---|---|
| candidate means | "no student may get more than X pages" | "every two cows at least X apart" |
| small candidate | too tight, needs too many students → no | easy → yes |
| big candidate | easy → yes | too demanding → no |
| line | N N N Y Y Y | Y Y Y N N N |
| we want | the first yes | the last yes |
| check(mid) yes | save, go left: high = mid - 1 | save, go right: low = mid + 1 |
| check(mid) no | go right: low = mid + 1 | go left: high = mid - 1 |
Part A · Brute force: try every page limit one by one
GeeksforGeeks · Allocate Minimum Pages
1The question in simple words
arr[i] is the number of pages in book i. There are k students. Hand out all the books so that:
- every student gets at least one book,
- each student gets a continuous run of books (like books 2, 3, 4, never books 1 and 3 without 2),
- no book is split or shared between two students.
For each way of handing out, look at the student with the most pages. Among all ways, pick the one where that number is smallest, and return it. If it can't be done (more students than books), return −1.
Example: arr = [12, 34, 67, 90], k = 2 → answer 113.
2What the constraints tell us
- n (number of books) up to 10⁶. One pass over the books is 10⁶ steps, so we can afford it a few dozen times, not thousands of times.
- arr[i] from 1 to 10³. So the total pages are at most 10⁶ × 10³ = 10⁹. That fits in an int, but it's large.
- k from 1 to 10³. k can be 1, meaning one student gets everything. k can also be bigger than n, and then some student would get no book → answer −1.
- More than about 10⁸ steps → TLE.
3Intuition
Listing every way to cut the row works only for tiny inputs. Instead, fix a limit X and ask: "if nobody may read more than X pages, how many students do I need?" Fill the first student's pile book by book until the next book would cross X, then start a new student. Count the students.
- If we need more than k students, X is too small.
- If k or fewer are enough, X is possible.
Try X = low, low + 1, …. The first X that is possible is the smallest maximum, which is our answer.
4Building the logic from examples
Step 1: list every split for k = 2 (the teacher's first step)
| student 1 | student 2 | pages (s1, s2) | max |
|---|---|---|---|
| 12 | 34, 67, 90 | 12, 191 | 191 |
| 12, 34 | 67, 90 | 46, 157 | 157 |
| 12, 34, 67 | 90 | 113, 90 | 113 ← smallest |
| 12, 34, 67, 90 | nothing | not allowed: student 2 gets no book | |
For each split we take the maximum (the busiest student). Then over all splits we take the minimum of those. min(191, 157, 113) = 113. "Minimise the maximum" in one table.
Step 2: the highest possible answer (high)
The teacher looks at the smallest k the constraints allow, k = 1. One student must take every book, so the answer is the whole sum, 12 + 34 + 67 + 90 = 203. With more students the load is shared and the answer can only go down. So no answer is ever above the total. high = sum(arr).
Step 3: the lowest possible answer (low)
She first says the answer lies somewhere from 1 to 203, then tightens it to start at 90, the biggest book. She leaves "why the max and not the min (12)?" as homework. Here's the answer:
→ Books can't be split. The student who gets the 90-page book reads at least 90 pages, whatever else happens. So any limit below 90 is impossible: that book fits in nobody's pile. Starting at 12 would only waste checks on 12…89, which are all no. (For k = n, every student gets exactly one book, and the answer is exactly the biggest book. So the max can really be the answer.)
So the answer lives on the line 90 … 203.
Step 4: the check with limit 90
- 12 → pile 12. Add 34 → 46 ≤ 90, keep.
- Add 67 → 113 > 90 ✗. Close this pile. A new student starts with 67.
- Add 90 → 157 > 90 ✗. A new student starts with 90.
- Piles: [12, 34] [67] [90] → 3 students needed, but we only have 2 → no.
Should we go higher or lower? With 90 we needed too many students. A bigger limit lets more books share a pile, so fewer students are needed. Go higher.
Step 5: the check with limit 150
12 + 34 + 67 = 113 ≤ 150. Adding 90 → 203 > 150, so a new pile starts with 90. Piles: [12, 34, 67] [90] → 2 students → yes. Now we'd like to see if something smaller (149, 148, …) still works. Go lower.
The yes/no line
→ Raising the limit never forces an extra student: every pile that fit under X also fits under X + 1. So the number of students needed only goes down (or stays the same) as X grows. Once it drops to k, it stays ≤ k. The teacher puts it like this: if 113 works, then 114, 115, … also work, and 113 is the smallest of them.
The check function, piece by piece
students = 1,pages_sum = 0: the first student is already open.- For each book: if
pages_sum + pages <= limit, the book joins the current pile. - Else: open a new student (
students += 1) and start that pile with this book:pages_sum = pages. - At the end:
students <= k→ yes.
students at 1 and not 0?→ We only add a student when a pile closes. The last pile never closes inside the loop. With limit 90, the loop adds 1 when 67 doesn't fit and 1 when 90 doesn't fit, which is 2 increments for 3 piles. Starting at 1 counts the first pile up front. The teacher says the other way works too: start at 0 and add 1 after the loop. She prefers starting at 1.
pages_sum = pages and not 0?→ The book that didn't fit (67) still has to go somewhere. It becomes the first book of the new student. If we reset to 0, the loop would move on to 90 and 67 would be lost, never counted.
→ If the piles fit within X using fewer than k students, we can always break a pile with 2+ books into two smaller piles. Each part is still ≤ X, and we keep doing this until there are exactly k students. That works as long as there are at least k books (k ≤ n). So "≤ k" is the right test, and it also makes the yes/no line monotonic.
→ Not with this check. At X = total sum, one pile holds everything, which is 1 student ≤ k, so the loop always returns something. The real impossible case is k > n (more students than books), and the check can't see it because it accepts fewer students. So we add
if k > len(arr): return -1 at the very top. Without it, [12, 34] with k = 3 would wrongly return 34.5Approach steps
- If k > n → return −1.
low = max(arr),high = sum(arr).- For limit = low … high: if the check says yes → return limit (the first yes is the smallest).
- (Unreachable, but kept from the teacher's code) return −1.
6Code (Python)
class Solution:
def findPages(self, arr, k):
if k > len(arr): # more students than books
return -1
low = max(arr) # the biggest book must fit somewhere
high = sum(arr) # one student reads everything
for limit in range(low, high + 1):
if self.isPossible(arr, k, limit):
return limit # first yes = smallest maximum
return -1
def isPossible(self, arr, k, limit):
students = 1
pages_sum = 0
for pages in arr:
if pages_sum + pages <= limit:
pages_sum += pages # same student takes this book
else:
students += 1 # new student...
pages_sum = pages # ...starting with THIS book
return students <= k7Code line by line
| line | what it means |
|---|---|
| if k > len(arr): return -1 | Someone would get no book, so it's impossible. (Our fix, see Doubt 4.) |
| low = max(arr) | No limit below the biggest book can work. |
| high = sum(arr) | The k = 1 answer. No answer is bigger. |
| for limit in range(low, high + 1): | Try limits from small to big. |
| return limit | We go upward and want the minimum, so the first yes is the answer. No need to save and continue. |
| students = 1 pages_sum = 0 | Student 1 is open with an empty pile. |
| if pages_sum + pages <= limit: | Would this book keep the current student within the limit? |
| pages_sum += pages | Yes → same student. |
| students += 1 pages_sum = pages | No → the next student starts, holding this book. |
| return students <= k | k students (or fewer) are enough at this limit. |
8Dry run (hand table)
arr = [12, 34, 67, 90], k = 2. low = 90, high = 203.
| limit | piles | students | check |
|---|---|---|---|
| 90 | [12, 34] [67] [90] | 3 | no |
| 91 … 112 | [12, 34] [67] [90] | 3 | no |
| 113 | [12, 34, 67] [90] | 2 | yes → return 113 |
24 checks (90 through 113), each walking all 4 books. Return 113 ✓.
9Complexity & remember
- Time O((sum − max) · n). The teacher counts the outer loop as about 10³ and the inner as 10⁶, so 10⁹ → TLE (and she shows the TLE on submit).
- Space O(1).
→ It runs from max(arr) to sum(arr), and the sum can reach 10⁶ × 10³ = 10⁹. So in the worst case the outer loop is close to 10⁹, not 10³, and the brute force is even slower than 10⁹ steps. The conclusion is the same: TLE, so optimise.
The inner walk can't be avoided, since every book has to be placed. But the limits low … high are a sorted number line with one N→Y flip, so the outer loop can be a binary search.
Part B · Optimal: binary search on the page limit
1The question
Same problem. Same low, high and check. The for loop becomes a binary search loop. Nothing else changes, as the teacher says.
2Constraints
Same as Part A. The range has at most about 10⁹ values, so about log₂(10⁹) ≈ 30 checks, each 10⁶ steps.
3Intuition
The line is N N N … Y Y Y and we want the first Y.
- check(mid) yes (≤ k students): mid might be the answer, so save it. But a smaller limit might also work, and we want the minimum → search left:
high = mid - 1. (Her example: 150 works, so try 149 and below.) - check(mid) no (too many students): mid is too small, and so is everything below it → search right:
low = mid + 1. (Her example: 90 fails, so look above 90.)
4Building the logic
→ A yes at 150 doesn't mean 150 is the smallest. We save it "because we don't know if a better one will turn up", then keep searching to the left. Each smaller yes overwrites it.
→ Ask "what am I trying to make smaller or bigger?" Here we minimise, so after a yes we look at smaller values (left). In Aggressive Cows we maximise, so after a yes we look at bigger values (right). A "no" always sends you toward the easier side.
The range shrinking
5Approach steps
- If k > n → return −1.
low = max(arr),high = sum(arr),answer = high.- While
low <= high:mid = low + (high - low) // 2. - isPossible(mid) →
answer = mid,high = mid - 1. Otherwise →low = mid + 1. - Return
answer.
6Code (Python)
class Solution:
def findPages(self, arr, k):
if k > len(arr):
return -1
low = max(arr)
high = sum(arr)
answer = high
while low <= high:
mid = low + (high - low) // 2 # candidate page limit
if self.isPossible(arr, k, mid):
answer = mid # works: save it...
high = mid - 1 # ...and try a smaller limit
else:
low = mid + 1 # too many students: go bigger
return answer
def isPossible(self, arr, k, limit):
students = 1
pages_sum = 0
for pages in arr:
if pages_sum + pages <= limit:
pages_sum += pages
else:
students += 1
pages_sum = pages
return students <= k7Code line by line
| line | what it means |
|---|---|
| answer = high | The sum always works (one pile), so it's a safe starting value. |
| while low <= high: | Keep going while limits remain untested. |
| mid = low + (high - low) // 2 | The middle limit (overflow-safe). |
| answer = mid high = mid - 1 | At mid, k students are enough. Keep it, and look for an even smaller limit on the left. |
| low = mid + 1 | mid needs too many students. Only bigger limits can work. |
| isPossible(...) | The same greedy student counter as in Part A. |
8Dry run (hand table)
arr = [12, 34, 67, 90], k = 2.
| step | low | high | mid | piles at mid | check(mid) | decision | thrown away |
|---|---|---|---|---|---|---|---|
| 1 | 90 | 203 | 146 | [12,34,67] [90] | yes (2) | answer = 146, high = 145 | 146…203 |
| 2 | 90 | 145 | 117 | [12,34,67] [90] | yes (2) | answer = 117, high = 116 | 117…145 |
| 3 | 90 | 116 | 103 | [12,34] [67] [90] | no (3) | low = 104 | 90…103 |
| 4 | 104 | 116 | 110 | [12,34] [67] [90] | no (3) | low = 111 | 104…110 |
| 5 | 111 | 116 | 113 | [12,34,67] [90] | yes (2) | answer = 113, high = 112 | 113…116 |
| 6 | 111 | 112 | 111 | [12,34] [67] [90] | no (3) | low = 112 | 111 |
| 7 | 112 | 112 | 112 | [12,34] [67] [90] | no (3) | low = 113 | 112 |
| end | 113 | 112 | low > high → return 113 ✓ | ||||
7 checks instead of the 24 the brute force needed.
9Complexity & remember
- Time O(n · log(sum − max)). The teacher's estimate: log₂(10³) ≈ 10 checks × 10⁶ = 10⁷ → passes. With the full range up to 10⁹, it's log₂(10⁹) ≈ 30 checks × 10⁶ = 3 × 10⁷. Still under 10⁸, so it passes either way.
- Space O(1).
Her tip for picking low and high: at first, find them by trying small cases by hand (k = 1 gave high, the biggest book gave low). After a few problems you start to see them straight away.
Part C · Revision page
| A · brute force | B · binary search | |
|---|---|---|
| range | low = max(arr), high = sum(arr) | |
| check | count the students greedily; yes if students ≤ k | |
| on yes | return immediately (first yes from the bottom) | save, high = mid - 1 |
| on no | try the next limit | low = mid + 1 |
| time | O(n · (sum − max)) (TLE) | O(n · log(sum − max)) |
| minimise the maximum (pages) | maximise the minimum (cows) | |
|---|---|---|
| low / high | max element / total sum | 1 / last − first |
| line | N N N Y Y Y | Y Y Y N N N |
| yes → | high = mid - 1 | low = mid + 1 |
| check passes when | groups ≤ k | cows placed ≥ k |
2. low = biggest book (it must fit somewhere), high = total pages (k = 1).
3. Check: fill each student until the next book would cross the limit, then a new student starts with that book.
4. Bigger limit → fewer students. Yes means students ≤ k.
5. Yes → save, go left. No → go right. k > n → −1.
✗ resetting the pile to 0 instead of to the current book (the book gets lost)
✗ starting
students at 0 and forgetting the last pile✗ testing
students == k instead of <= k✗ going right on yes (that's the max-of-min direction)
✗ forgetting the k > n → −1 case
s = Solution() print(s.findPages([12, 34, 67, 90], 2)) # 113 print(s.findPages([15, 17, 20], 5)) # -1 print(s.findPages([22, 23, 67], 1)) # 112 print(s.findPages([15, 10, 19, 10, 5, 18, 7], 5)) # 25
Based on this video: Allocate Minimum Number of Pages | Binary Search on Answer