EssayAI
Блог
Блог
Математика и алгоритмы

Бинарный поиск по ответу: метод и примеры задач

11 июня 2026Время чтения: 7 минут
#бинарный поиск#поиск по ответу#алгоритмы#оптимизация#монотонность

Бинарный поиск по ответу - один из самых мощных алгоритмических приёмов: вместо прямого перебора кандидатов мы бинарно ищем само значение ответа на числовой оси. Ключевое условие - существование монотонного предиката: функция feasible(x) принимает значение «да» при xxx \le x^* и «нет» при x>xx > x^* (или наоборот). Это превращает задачу оптимизации в задачу поиска точки перехода, которую классический бинарный поиск находит за O(logN)O(\log N) шагов. Чтобы почувствовать геометрию метода, покрутите калькулятор ниже - он покажет, как делится пространство ответов на каждой итерации.

Идея метода: от перебора к делению пополам

Классический бинарный поиск ищет элемент в отсортированном массиве. Бинарный поиск по ответу - ищет не элемент, а само значение ответа xx^* в числовом диапазоне [lo,hi][lo, hi]. Вместо проверки «есть ли xx в массиве» используется предикат P(x)P(x): «можно ли достичь ответа xx при данных ограничениях».

Монотонность предиката - обязательное условие: если P(x)P(x) истинно, то P(x)P(x') истинно для всех xxx' \le x (при поиске минимума). Именно это позволяет отбрасывать половину диапазона на каждом шаге.

Схема алгоритма:

Числовая ось ответов делится пополам: зелёные клетки - значения, для которых предикат выполнен, красные - нет. Граница ищется бинарным поиском: каждая итерация вдвое сужает диапазон

Структура кода на любом языке практически одинакова:

lo=мин. возможный ответ,hi=макс. возможный ответlo = \text{мин. возможный ответ}, \quad hi = \text{макс. возможный ответ} while lo<hi:mid=(lo+hi)/2\text{while } lo < hi: \quad mid = \lfloor (lo + hi) / 2 \rfloor if P(mid) истинноhi=mid,иначеlo=mid+1\text{if } P(mid) \text{ истинно} \Rightarrow hi = mid, \quad \text{иначе} \Rightarrow lo = mid + 1

Ответ - значение lolo в конце (наименьший xx, при котором P(x)P(x) истинно).

Как формулировать предикат

Правильный предикат - половина решения. Нужно перефразировать задачу: «при данном значении ответа xx - можно ли выполнить условие задачи?»

Типовые примеры:

  • «Найти минимальное время, за которое kk рабочих выполнят nn задач» → предикат: «за время xx рабочие выполняют не менее nn задач» → проверяется за O(k)O(k).
  • «Найти минимальный максимум нагрузки при разбивке массива на mm частей» → предикат: «можно ли разбить массив на mm частей, каждая с суммой не более xx» → жадная проверка за O(n)O(n).
  • «Найти kk-е наименьшее расстояние между парами точек» → предикат: «количество пар с расстоянием x\le x не меньше kk» → проверка за O(nlogn)O(n \log n).
Монотонная функция числа выполненных задач от лимита времени x: слева предикат ложен, справа - истинен; граница x* - минимальный допустимый ответ
Монотонная функция числа выполненных задач от лимита времени x: слева предикат ложен, справа - истинен; граница x* - минимальный допустимый ответ

Предикат должен быть:

  1. Монотонным: если P(x)P(x) истинно, то P(x+1)P(x+1) тоже истинно (при поиске минимума).
  2. Вычислимым: проверка одного значения xx занимает не более O(nlogn)O(n \log n), иначе суммарная сложность O(nlognlogN)O(n \log n \cdot \log N) становится неприемлемой.

Целочисленный и вещественный случаи

Выбор типа границ влияет на схему поиска.

Целочисленный поиск (поиск наименьшего xx, при котором предикат истинен):

lo=0,hi=N,mid=(lo+hi)/2lo = 0, \quad hi = N, \quad mid = \lfloor (lo + hi) / 2 \rfloor P(mid) истинноhi=mid;P(mid) ложноlo=mid+1P(mid) \text{ истинно} \Rightarrow hi = mid; \quad P(mid) \text{ ложно} \Rightarrow lo = mid + 1

Инвариант: ответ всегда в [lo,hi][lo, hi]. Цикл заканчивается при lo=hilo = hi - это и есть ответ.

Вещественный поиск (например, найти радиус круга):

while hilo>ε:mid=(lo+hi)/2\text{while } hi - lo > \varepsilon: \quad mid = (lo + hi) / 2

Достаточно 50-100 итераций для точности 101510^{-15} при любом начальном диапазоне. Ошибка ε\varepsilon выбирается на порядок меньше нужной точности ответа.

Оценка сложности

Пусть диапазон ответов - [0,N][0, N], а проверка предиката занимает T(n)T(n) времени. Тогда:

Число итераций=log2N\text{Число итераций} = \lceil \log_2 N \rceil Итоговая сложность=O(T(n)logN)\text{Итоговая сложность} = O(T(n) \cdot \log N)

Для типичных задач: если предикат проверяется за O(n)O(n), то весь алгоритм работает за O(nlogN)O(n \log N) - логарифмическое ускорение по сравнению с перебором O(nN)O(nN).

Частая ошибка: выбирать диапазон [0, n] вместо реального диапазона ответа. Если ответ может быть 10^9, то hi = n даст неверный результат. Всегда думайте: каков физический смысл минимального и максимального допустимого ответа.

Классические задачи

Задача 1: Разбивка книги на страницы. Дан массив из nn чисел (страниц в главах книги). Нужно разбить на mm частей так, чтобы максимальная сумма части была минимальной.

Предикат: «Можно ли разбить на mm частей с суммой каждой x\le x?». Проверяется жадно за O(n)O(n): идём слева направо, открываем новую часть, когда текущая сумма превысит xx.

Диапазон: lo=max(ai)lo = \max(a_i) (одна часть из одного элемента), hi=aihi = \sum a_i (вся книга - одна часть).

Задача 2: kk рабочих, nn задач с временами tit_i. Каждый рабочий берёт задачи с суммой времени не более xx.

Предикат: «Успеют ли kk рабочих выполнить все задачи за время xx?». Жадно: сортируем задачи по убыванию, раздаём рабочему с наименьшей нагрузкой - или просто считаем x/tin\sum \lfloor x / t_i \rfloor \ge n.

Задача 3: kk-е минимальное произведение. В двух отсортированных массивах найти kk-е наименьшее произведение пары.

Предикат: «Сколько пар (ai,bj)(a_i, b_j) имеют произведение x\le x?». Для каждого aia_i - бинарный поиск по bjb_j, итого O(nlogn)O(n \log n).

Связь с параметрическим поиском

Бинарный поиск по ответу - частный случай параметрического поиска: мы параметризуем задачу значением xx и сводим оптимизацию к серии задач принятия решений. Этот подход объединяет несколько классов задач:

  • Минимакс: минимизировать максимум чего-либо (нагрузки, расстояния, разности элементов).
  • Максимин: максимизировать минимум (максимальный минимальный промежуток между точками при выборе kk объектов).
  • Задачи на kk-й порядковый статистик: найти kk-й по величине элемент в структуре, где прямой доступ невозможен.

Во всех этих случаях предикат формулируется единообразно: «при пороге xx условие выполнено?». Разные задачи отличаются только тем, что именно подставить в это «условие» и как его проверить. Освоив шаблон бинарного поиска по ответу один раз, вы получаете инструмент, который применим к сотням соревновательных задач без изменения каркаса алгоритма.

Частые ошибки

  • Неверные границы диапазона. Если lolo или hihi не покрывают реальный ответ, алгоритм даёт мусор. Всегда проверяйте: P(lo)P(lo) ложно, P(hi)P(hi) истинно (или наоборот) до запуска цикла.
  • Не та схема обновления границ. Для поиска минимального xx с P(x)=trueP(x)=\text{true}: P(mid)P(mid) истинно hi=mid\Rightarrow hi=mid, иначе lo=mid+1\Rightarrow lo=mid+1. Для максимального: наоборот. Перепутав, получите бесконечный цикл или неверный ответ.
  • Целочисленное переполнение при вычислении mid. Вместо (lo+hi)/2(lo+hi)/2 безопаснее lo+(hilo)/2lo + (hi-lo)/2 - сумма lo+hilo+hi может переполнить 32-битный тип.
  • Немонотонный предикат. Если из P(x)=trueP(x)=\text{true} не следует P(x+1)=trueP(x+1)=\text{true}, бинарный поиск по ответу неприменим. Сначала докажите монотонность.
  • Вещественная точность. При вещественном бинарном поиске нельзя использовать while lo < hi - вещественные числа могут не сойтись. Нужно while hi - lo > eps или фиксированное число итераций (50-100).

FAQ

Чем бинарный поиск по ответу отличается от обычного? Обычный бинарный поиск ищет заданный элемент в отсортированном массиве. Поиск по ответу ищет само значение ответа: нет никакого массива, есть только диапазон возможных ответов и функция проверки. Метод применим, когда на вопрос «можно ли достичь результата xx» можно ответить быстро, а множество допустимых xx монотонно.

Всегда ли предикат должен быть монотонным? Да, это обязательное условие. Если функция «допустимости» меняет значение несколько раз на числовой оси, классический бинарный поиск даст неверный результат. В таких случаях нужны другие методы - тернарный поиск (для унимодальных функций) или разбиение на монотонные участки.

Когда использовать вещественный, а когда целочисленный бинарный поиск по ответу? Целочисленный - когда ответ целое число (количество, индекс, число шагов). Вещественный - когда ответ непрерывен (расстояние, время, минимальный радиус). Граница: если в задаче написано «найти наименьшее целое kk» - целочисленный; «найти минимальное расстояние» - вещественный с точностью 10610^{-6} или лучше.

Коротко

Бинарный поиск по ответу работает там, где ответ xx^* лежит на числовой оси, а предикат P(x)P(x) - монотонен: истинен для всех xxx \ge x^* и ложен для x<xx < x^* (при поиске минимума). Алгоритм выполняет O(logN)O(\log N) итераций, на каждой проверяя предикат - итоговая сложность O(T(n)logN)O(T(n) \cdot \log N). Ключевые шаги: сформулировать монотонный предикат, выбрать правильные границы диапазона и верную схему обновления lolo/hihi.

Доверьте текст нейросети EssayAI

Открыть EssayAI

Бесплатно, на русском языке и без VPN

Читайте также