Жадные алгоритмы

Жадность работает, когда локально лучший шаг ведёт к глобально лучшему ответу.

Жадный алгоритм — стратегия, на каждом шаге делающая локально оптимальный выбор в надежде получить глобальный оптимум.

Когда жадность работает

Не всегда! Жадность корректна только если у задачи есть свойство жадного выбора: локально лучший шаг не закрывает путь к оптимуму. Для размена монет произвольными номиналами жадность может ошибаться, а вот для классических задач планирования — работает и даёт O(n log n) вместо перебора.

Классическая задача: максимум непересекающихся интервалов

Дан набор интервалов (встреч). Сколько максимум можно провести, чтобы они не пересекались? Жадная идея: всегда выбирать встречу, которая раньше всех заканчивается — она оставляет больше места для остальных. Сортируем по правому концу и жадно набираем.

def max_meetings(intervals):
    intervals.sort(key=lambda x: x[1])  # по времени окончания
    count = 0
    last_end = float("-inf")
    for start, end in intervals:
        if start >= last_end:
            count += 1
            last_end = end
    return count

print(max_meetings([(1, 3), (2, 4), (3, 5), (0, 6)]))
print(max_meetings([(1, 2), (2, 3), (3, 4)]))
print(max_meetings([(1, 5), (1, 5), (1, 5)]))

Вывод:

2
3
1

Как работает под капотом

Почему «раньше заканчивается» — правильная жадность? Среди всех совместимых встреч та, что заканчивается первой, оставляет максимум свободного времени дальше. Можно доказать обменом: в любом оптимальном расписании первую встречу можно заменить на самую рано заканчивающуюся, не ухудшив ответа. Дальше задача повторяется на меньшем диапазоне — это и есть оптимальная подструктура плюс жадный выбор. Сортировка даёт O(n log n), сам проход — O(n).

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

  • Сортировать по началу или по длительности вместо конца — даёт неоптимальный ответ.
  • Применять жадность там, где нужно DP (например, размен произвольными монетами).
  • Не доказать жадный выбор и понадеяться «на интуицию» — интервьюер попросит обоснование.

Итог

  • Жадность делает локально лучший выбор; корректна не для всех задач.
  • В задаче об интервалах сортируйте по концу и набирайте непересекающиеся.
  • Готовьтесь обосновать, почему жадный выбор приводит к оптимуму.
Проверьте себя
1. По какому критерию сортировать интервалы в задаче о максимуме непересекающихся встреч?
AПо времени начала
BПо длительности
CПо времени окончания
DПо количеству участников
2. Когда жадный алгоритм гарантированно даёт оптимум?
AВсегда
BКогда у задачи есть свойство жадного выбора и оптимальная подструктура
CТолько для отсортированных данных
DНикогда, нужна проверка перебором