7.4. Задача «Сбор подписей»

В этом параграфе вы разберёте ещё один вариант задачи с ограниченными ресурсами. Но в отличие от предыдущего примера, где критерий выбора был простым и однозначным, здесь придётся искать более тонкую стратегию: оценивать выгоду на единицу ресурса. Это позволяет использовать жадный алгоритм там, где «просто отсортировать» уже недостаточно. Главная цель задачи — научиться доказывать, что выбранный критерий действительно ведёт к оптимальному решению.

Ключевые вопросы параграфа

  • Чем задача «Сбор подписей» отличается от других задач с ограниченными ресурсами?
  • Почему стратегия «максимальная прибыль на единицу ресурса» оказывается оптимальной?
  • Как корректно оформить и проверить такое решение?

Algoritmy

Максимальная выгода при ограниченных ресурсах

На этот раз ваша задача — собрать подписи со всех жильцов дома. Вам известно время, в которое каждый из них будет у себя. Естественно, вам хочется собрать все подписи, заходя в дом минимальное количество раз. Для простоты давайте предположим, что вы, зайдя в дом, собираете подписи сразу со всех жильцов, которые на месте.

В дальнейшем под сегментом будем понимать интервал времени нахождения жильца в доме. Количество жильцов будет соответствовать количеству сегментов.

  • Входные данные: Количество сегментов в первой строке — . Каждая из следующих строк содержит два целых числа и (разделены пробелом), которые указывают на координаты границ -го сегмента.
  • Выходные данные: Минимальное количество точек на первой строке и координаты точек целыми числами (разделены пробелом) на второй строке. Выводить точки можно в любом порядке. При наличии нескольких наборов точек, можно вывести любой из них.
  • Ограничения: ; для всех .

Пример 1

Ввод

Вывод

3
1 3
2 5
3 6

1
3

Все три сегмента , , содержат точку с координатами 3.

Пример 2

Ввод

Вывод

4
4 7
1 3
2 5
5 6

2
3 6

Второй и третий сегменты содержат точку с координатами , в то время как первый и четвертый содержат точку с координатами . Одной точкой покрыть все сегменты нельзя, так как и не пересекаются. В этом случае есть еще одно верное решение — точки 2 и 5.

Решение

Решение заключается в выявлении сегмента с наименьшим значением правой границы.

Самое маленькое значение границы сегмента: . Мы утверждаем, что существует оптимальное решение, включающее в себя точку . Чтобы доказать это, возьмём оптимальное решение . Оно должно покрывать сегмент , поэтому содержит точку , что приводит к . Если , то наша работа закончена. Иначе . В этом случае мы можем заменить на в .

Algosy

Понятно, что это не меняет размер решения . Чтобы доказать, что всё ещё является решением, подойдём от противного и предположим, что некий сегмент покрывается , но не покрывается . Это означает и противоречит тому, что — самое маленькое значение правой границы.

Таким образом, мы приходим к следующему алгоритму:

  • добавить в решение минимальное значение правой границы ,
  • отбросить все сегменты, покрытые ,
  • повторить.
1SegmentsCover(segments):
2    points←empty set
3    while segments is not empty:
4        r_m = minimum right endpoint of a segment from segments
5        add r_m points
6        remove segments covered by r_m from the set segments
7    return points

На рисунке ниже показан пример.

Algoritmy

Время выполнения составит , где , так как используется не более итераций цикла while (при каждой итерации отбрасывается как минимум один сегмент), и каждая итерация сводится к проверке списка (одним сканированием находится значение , а другим убираются сегменты, покрываемые ).

Этот алгоритм уже достаточно быстрый, чтобы успешно пройти оценку. Однако можно дополнительно сократить время выполнения с до , если отсортировать сегменты от малых до больших значений правой границы и затем просто просканировать список один раз.

Что дальше

Теперь вы умеете применять жадные алгоритмы, когда нужно максимизировать результат при ограниченном ресурсе, используя критерий эффективности на единицу ресурса.

Далее — задача с другим типом цели: теперь ресурс нужно распределить так, чтобы увеличить количество получателей. Вы узнаете, как простая сортировка помогает достичь справедливого распределения и почему жадный подход оказывается удачным и в этой ситуации.

А пока вы не ушли дальше — закрепите материал на практике:

  • Отметьте, что урок прочитан, при помощи кнопки ниже.
  • Пройдите мини-квиз, чтобы проверить, насколько хорошо вы усвоили тему.
  • Перейдите к задачам этого параграфа и потренируйтесь.
  • Перед этим — загляните в короткий гайд о том, как работает система проверки.

Хотите обсудить, задать вопрос или не понимаете, почему код не работает? Мы всё предусмотрели — вступайте в сообщество Хендбука! Там студенты помогают друг другу разобраться.

Ключевые выводы параграфа

  • Жадный алгоритм может работать не только с абсолютными значениями, но и с относительными критериями, например «выгода на единицу ресурса».
  • Оптимальность жадного критерия нужно обосновывать — иначе легко получить неверное решение.
  • Работа с вещественными числами требует особой аккуратности в сравнении и реализации.
Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E
Предыдущий параграф7.3. Задача «Рекламная кампания»
Следующий параграф7.5. Задача «Количество призов»