Searching & SortingProblem 25 of 36
Book Allocation Problem
Problem Statement
Given an array of integers books[] where books[i] represents the number of pages in the i-th book, and an integer m representing the number of students, allocate books to students such that:
- Each student gets at least one book
- Each book is allocated to exactly one student
- Books are allocated in contiguous order
- The maximum number of pages allocated to any student is minimized
Return the minimum possible value of the maximum pages allocated.
Constraints:
- 1 ≤ n ≤ 10^5
- 1 ≤ books[i] ≤ 10^6
- 1 ≤ m ≤ n
Example:
- Input:
books = [12, 34, 67, 90],m = 2 - Output:
113 - Explanation: Allocation: [12, 34, 67] and [90] → max = 113. Or [12] and [34, 67, 90] → max = 191. Optimal is 113.
Example 2:
- Input:
books = [10, 20, 30, 40],m = 2 - Output:
60 - Explanation: [10, 20, 30] = 60 and [40] = 40. Maximum = 60
Approach 1: Brute Force (Recursion)
Intuition
Try all possible ways to partition the array into m contiguous parts and find the partition that minimizes the maximum sum among all parts.
Algorithm
- Use recursion to try all possible partitions
- For each partition, calculate the maximum sum
- Track the minimum of all maximum sums
- Base case: if only one student left, give all remaining books
java
import java.util.*;
public class Solution {
private int minPages = Integer.MAX_VALUE;
private void solve(int[] books, int idx, int m, int currentMax) {
int n = books.length;
// Base case: last student gets remaining books
if (m == 1) {
int sum = 0;
for (int i = idx; i < n; i++) {
sum += books[i];
}
minPages = Math.min(minPages, Math.max(currentMax, sum));
return;
}
int sum = 0;
for (int i = idx; i <= n - m; i++) {
sum += books[i];
solve(books, i + 1, m - 1, Math.max(currentMax, sum));
}
}
public int allocateBooks(int[] books, int m) {
if (m > books.length) return -1;
minPages = Integer.MAX_VALUE;
solve(books, 0, m, 0);
return minPages;
}
}Complexity Analysis
- Time Complexity: O(n^n) - Exponential due to trying all possible partitions
- Space Complexity: O(n) - Recursion stack depth
Approach 2: Binary Search on Answer (Optimal)
Intuition
The key insight is that if we can allocate books such that no student gets more than X pages, we can also allocate with any limit > X. This monotonic property allows binary search on the answer.
Key Observations:
- Minimum possible answer = max(books[i]) (single largest book)
- Maximum possible answer = sum(books[i]) (all books to one student)
- For a given limit, greedily check if allocation is possible
- Binary search between min and max to find optimal answer
Algorithm
- Set low = max(books), high = sum(books)
- Binary search on the maximum pages limit
- For each mid, check if we can allocate with at most mid pages per student
- If possible, try smaller limit; otherwise, increase limit
java
import java.util.*;
public class BookAllocation {
private boolean canAllocate(int[] books, int m, int maxPages) {
int students = 1;
int currentSum = 0;
for (int pages : books) {
// Single book exceeds limit
if (pages > maxPages) return false;
if (currentSum + pages > maxPages) {
// Allocate to next student
students++;
currentSum = pages;
if (students > m) return false;
} else {
currentSum += pages;
}
}
return true;
}
public int allocateBooks(int[] books, int m) {
int n = books.length;
if (m > n) return -1;
int low = 0, high = 0;
for (int pages : books) {
low = Math.max(low, pages);
high += pages;
}
int result = high;
while (low <= high) {
int mid = low + (high - low) / 2;
if (canAllocate(books, m, mid)) {
result = mid;
high = mid - 1; // Try smaller limit
} else {
low = mid + 1; // Need larger limit
}
}
return result;
}
public static void main(String[] args) {
BookAllocation ba = new BookAllocation();
int[] books = {12, 34, 67, 90};
System.out.println(ba.allocateBooks(books, 2)); // Output: 113
}
}Dry Run Example
books = [12, 34, 67, 90], m = 2
low = max(12, 34, 67, 90) = 90
high = 12 + 34 + 67 + 90 = 203
Iteration 1:
mid = (90 + 203) / 2 = 146
canAllocate(146)?
Student 1: 12 + 34 + 67 = 113 ≤ 146
Student 1: 113 + 90 = 203 > 146, assign 90 to Student 2
Student 2: 90 ≤ 146
Total students = 2 ≤ m. Return true.
result = 146, high = 145
Iteration 2:
mid = (90 + 145) / 2 = 117
canAllocate(117)?
Student 1: 12 + 34 + 67 = 113 ≤ 117
Student 1: 113 + 90 = 203 > 117, assign 90 to Student 2
Student 2: 90 ≤ 117
Total students = 2 ≤ m. Return true.
result = 117, high = 116
Iteration 3:
mid = (90 + 116) / 2 = 103
canAllocate(103)?
Student 1: 12 + 34 = 46 ≤ 103
Student 1: 46 + 67 = 113 > 103, assign 67 to Student 2
Student 2: 67 + 90 = 157 > 103, assign 90 to Student 3
Total students = 3 > m. Return false.
low = 104
Iteration 4:
mid = (104 + 116) / 2 = 110
canAllocate(110)?
Student 1: 12 + 34 = 46 ≤ 110
Student 1: 46 + 67 = 113 > 110, assign 67 to Student 2
Student 2: 67 + 90 = 157 > 110, assign 90 to Student 3
Total students = 3 > m. Return false.
low = 111
Iteration 5:
mid = (111 + 116) / 2 = 113
canAllocate(113)?
Student 1: 12 + 34 + 67 = 113 ≤ 113
Student 1: 113 + 90 = 203 > 113, assign 90 to Student 2
Student 2: 90 ≤ 113
Total students = 2 ≤ m. Return true.
result = 113, high = 112
Iteration 6:
low = 111, high = 112
mid = 111
canAllocate(111)?
Student 1: 12 + 34 = 46 ≤ 111
Student 1: 46 + 67 = 113 > 111, assign 67 to Student 2
Total students = 3 > m. Return false.
low = 112
Iteration 7:
mid = 112
canAllocate(112)?
Similar analysis... Return false.
low = 113
low > high, loop ends.
Result: 113
Allocation: [12, 34, 67] (sum=113) and [90] (sum=90)
Complexity Analysis
- Time Complexity: O(n log(sum - max))
- Binary search runs O(log(sum - max)) times
- Each canAllocate check takes O(n)
- Space Complexity: O(1) - Only using variables
Variant: Return the Actual Allocation
Key Takeaways
- Binary Search on Answer: When minimizing maximum (or maximizing minimum), binary search on the answer space
- Monotonic Property: If limit X works, all limits > X also work
- Greedy Verification: Use greedy approach to check if a limit is feasible
- Search Space Bounds: min = max(individual), max = sum(all)
- Edge Cases: Handle m > n (impossible allocation)
- Similar Problems: Painter's Partition, Split Array Largest Sum, Capacity to Ship Packages
- Pattern Recognition: "Minimize maximum" or "Maximize minimum" often indicates binary search on answer