Разбираем задачу распределения книг с бинарным поиском по ответу. Учимся минимизировать максимум страниц у студента. Код на Python.
Задача «Распределение книг» — классика собеседований в FAANG. Она учит применять бинарный поиск не к массиву, а к диапазону возможных ответов. Освоив этот паттерн, вы сможете решать десятки похожих задач: Aggressive Cows, Painter's Partition, Koko Eating Bananas и другие.
Даны массив books, где books[i] — количество страниц в i-й книге, и число m студентов. Нужно распределить книги так, чтобы:
Верните минимально возможное значение максимума страниц.
На собеседовании можно начать с наивного подхода: перебрать все возможные лимиты страниц X от max(books) до sum(books) и проверить, можно ли распределить книги так, чтобы ни один студент не получил больше X страниц. Сложность O(N × Sum) — слишком медленно для больших сумм.
Заметим, что ответ лежит в диапазоне от max(books) (самая толстая книга) до sum(books) (все книги у одного студента). Главная идея: мы не перебираем все значения, а используем бинарный поиск по этому диапазону. Для каждого среднего значения mid проверяем, возможно ли распределение с лимитом mid. Если да — пробуем уменьшить лимит, если нет — увеличиваем.
Ключевое свойство, делающее бинарный поиск возможным: если распределение возможно при лимите X, то оно возможно и при любом Y > X. Монотонность гарантирует корректность поиска.
Проверка работает за O(N): мы последовательно выделяем книги студентам, пока не превысим лимит max_pages. Как только превышение — переходим к следующему студенту. Если количество студентов не превышает m — распределение возможно.
def is_possible(books, m, max_pages):
students = 1
pages = 0
for book in books:
if pages + book <= max_pages:
pages += book
else:
students += 1
pages = book
return students <= mdef allocate_books(books, m):
if m > len(books):
return -1 # студентов больше, чем книг — невозможно
low = max(books) # минимальный возможный ответ
high = sum(books) # максимальный возможный ответ
while low < high:
mid = (low + high) // 2
if is_possible(books, m, mid):
high = mid # пробуем уменьшить лимит
else:
low = mid + 1 # увеличиваем лимит
return lowПусть books = [12, 34, 67, 90], m = 2.
Если в условии задачи есть фразы «минимизировать максимум» или «максимизировать минимум», и вы можете быстро проверить, возможен ли ответ, — используйте бинарный поиск по диапазону ответов. Этот паттерн встречается в задачах:
Прямо сейчас откройте LeetCode и решите задачу 1011. Capacity To Ship Packages Within D Days. Примените тот же шаблон: бинарный поиск по весу груза, функция проверки за O(N). Закрепите паттерн на трёх-четырёх задачах — и вы будете узнавать его в любой формулировке.
Запомните ментальную модель: «Могу ли я распределить, если максимум не превышает X?» Если да — уменьшаем X, если нет — увеличиваем. Бинарный поиск по ответу — ваш новый супер-инструмент.
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →