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 · 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

the safe way to find the middle
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

  1. Answer range: the smallest possible answer (low) and the largest (high).
  2. Yes/no check: test one candidate. Here: "if no student may read more than X pages, can k students cover all the books?"
  3. 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 candidatetoo tight, needs too many students → noeasy → yes
big candidateeasy → yestoo demanding → no
lineN N N Y Y YY Y Y N N N
we wantthe first yesthe last yes
check(mid) yessave, go left: high = mid - 1save, go right: low = mid + 1
check(mid) nogo right: low = mid + 1go 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:

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.

index0123
pages12346790

2What the constraints tell us

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.

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 1student 2pages (s1, s2)max
1234, 67, 9012, 191191
12, 3467, 9046, 157157
12, 34, 6790113, 90113 ← smallest
12, 34, 67, 90nothingnot 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:

Doubt (her homework): why is low the largest book, not the smallest?
→ 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

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

limit9091…111112113114…202203
students33…3322…21
≤ k?NN…NNYY…YYanswer = the first Y = 113
Doubt: why does it flip only once?
→ 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

Doubt 1: why start 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.
Doubt 2: when a new pile starts, why set 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.
Doubt 3: why is "fewer than k students" also a yes? Every student must get a book!
→ 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.
Doubt 4 (a fix): the teacher's brute force returns −1 "if no limit worked". Can that ever happen?
→ 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

  1. If k > n → return −1.
  2. low = max(arr), high = sum(arr).
  3. For limit = low … high: if the check says yes → return limit (the first yes is the smallest).
  4. (Unreachable, but kept from the teacher's code) return −1.

6Code (Python)

Allocate Minimum Pages, brute force
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 <= k

7Code line by line

linewhat it means
if k > len(arr): return -1Someone 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 limitWe go upward and want the minimum, so the first yes is the answer. No need to save and continue.
students = 1 pages_sum = 0Student 1 is open with an empty pile.
if pages_sum + pages <= limit:Would this book keep the current student within the limit?
pages_sum += pagesYes → same student.
students += 1 pages_sum = pagesNo → the next student starts, holding this book.
return students <= kk students (or fewer) are enough at this limit.

8Dry run (hand table)

arr = [12, 34, 67, 90], k = 2. low = 90, high = 203.

limitpilesstudentscheck
90[12, 34] [67] [90]3no
91 … 112[12, 34] [67] [90]3no
113[12, 34, 67] [90]2yes → return 113

24 checks (90 through 113), each walking all 4 books. Return 113 ✓.

9Complexity & remember

Doubt (a correction): is the outer loop really only 10³?
→ 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.

Remember brute forcelow = max, high = sum. Check = count the students greedily (start at 1; on overflow, new student starting with this book). The first limit where students ≤ k is the answer.

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.

4Building the logic

Doubt 1: why save mid on yes instead of returning it?
→ 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.
Doubt 2: this is the opposite of Aggressive Cows. How do I remember which way to go?
→ 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

step 190…146…203mid 146 → yes → save, drop 146…203
step 290…117…145146…203mid 117 → yes → save, drop 117…145
step 390…103…116117…203mid 103 → no → drop 90…103
step 490…103104…110…116mid 110 → no → drop 104…110
step 590…110111112113…116mid 113 → yes → save, drop 113…116
steps 6–790…110111112113…203111 no, 112 no → low passes high → answer 113

5Approach steps

  1. If k > n → return −1.
  2. low = max(arr), high = sum(arr), answer = high.
  3. While low <= high: mid = low + (high - low) // 2.
  4. isPossible(mid) → answer = mid, high = mid - 1. Otherwise → low = mid + 1.
  5. Return answer.

6Code (Python)

Allocate Minimum Pages, binary search on the answer
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 <= k

7Code line by line

linewhat it means
answer = highThe 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) // 2The middle limit (overflow-safe).
answer = mid high = mid - 1At mid, k students are enough. Keep it, and look for an even smaller limit on the left.
low = mid + 1mid 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.

steplowhighmidpiles at midcheck(mid)decisionthrown away
190203146[12,34,67] [90]yes (2)answer = 146, high = 145146…203
290145117[12,34,67] [90]yes (2)answer = 117, high = 116117…145
390116103[12,34] [67] [90]no (3)low = 10490…103
4104116110[12,34] [67] [90]no (3)low = 111104…110
5111116113[12,34,67] [90]yes (2)answer = 113, high = 112113…116
6111112111[12,34] [67] [90]no (3)low = 112111
7112112112[12,34] [67] [90]no (3)low = 113112
end113112low > high → return 113 ✓

7 checks instead of the 24 the brute force needed.

9Complexity & remember

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.

Remember the optimal waylow = max, high = sum. Binary search the limit. Yes (students ≤ k) → save and go left (we want smaller). No → go right.

Part C · Revision page

A · brute forceB · binary search
rangelow = max(arr), high = sum(arr)
checkcount the students greedily; yes if students ≤ k
on yesreturn immediately (first yes from the bottom)save, high = mid - 1
on notry the next limitlow = mid + 1
timeO(n · (sum − max)) (TLE)O(n · log(sum − max))
minimise the maximum (pages)maximise the minimum (cows)
low / highmax element / total sum1 / last − first
lineN N N Y Y YY Y Y N N N
yes →high = mid - 1low = mid + 1
check passes whengroups ≤ kcows placed ≥ k
If you remember only 5 lines 1. The answer (113) isn't in the array → binary search on a range of answers.
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.
Mistakes to avoid ✗ low = min(arr) (wastes checks) or low = 0
✗ 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
test it yourself (paste under any of the solutions above)
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