Files
hello-algo/ru/docs/chapter_greedy/max_capacity_problem.md
2026-01-20 15:08:42 +08:00

9.1 KiB
Raw Blame History

Задача о максимальной вместимости

!!! question

Дан массив $ht$, каждый элемент которого представляет высоту вертикальной перегородки. Любые две перегородки в массиве и пространство между ними могут образовать контейнер.

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

Необходимо выбрать в массиве две перегородки так, чтобы вместимость образованного контейнера была максимальной, и вернуть максимальную вместимость. Пример показан на рисунке ниже.

Пример данных для задачи о максимальной вместимости

Контейнер образован любыми двумя перегородками, поэтому состоянием в данной задаче являются индексы двух перегородок, обозначим их как $[i, j]$.

Согласно условию задачи, вместимость равна произведению высоты на ширину, где высота определяется короткой перегородкой, а ширина — разностью индексов двух перегородок в массиве. Обозначим вместимость как cap[i, j], тогда можно получить формулу для расчета:


cap[i, j] = \min(ht[i], ht[j]) \times (j - i)

Пусть длина массива равна n, количество комбинаций двух перегородок (общее число состояний) составляет C_n^2 = \frac{n(n - 1)}{2}. Наиболее прямой подход — перебрать все состояния, чтобы найти максимальную вместимость, временная сложность составит O(n^2).

Определение жадной стратегии

Для этой задачи существует более эффективное решение. Как показано на рисунке ниже, выберем состояние [i, j], которое удовлетворяет условию i < j и ht[i] < ht[j], то есть i — короткая перегородка, j — длинная перегородка.

Начальное состояние

Как показано на рисунке ниже, если сейчас переместить длинную перегородку j ближе к короткой перегородке i, вместимость обязательно уменьшится.

Это происходит потому, что после перемещения длинной перегородки j ширина j-i определенно уменьшится; а высота определяется короткой перегородкой, поэтому высота может только остаться неизменной (i остается короткой перегородкой) или уменьшиться (перемещенная j становится короткой перегородкой).

Состояние после перемещения длинной перегородки внутрь

Рассуждая в обратном направлении, только сжимая короткую перегородку i внутрь, мы можем увеличить вместимость. Потому что хотя ширина определенно уменьшится, высота может увеличиться (перемещенная короткая перегородка i может стать длиннее). Например, на рисунке ниже после перемещения короткой перегородки площадь увеличивается.

Состояние после перемещения короткой перегородки внутрь

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

На рисунке ниже показан процесс выполнения жадной стратегии.

  1. В начальном состоянии указатели i и j находятся на противоположных концах массива.
  2. Вычислить вместимость текущего состояния cap[i, j] и обновить максимальную вместимость.
  3. Сравнить высоты перегородок i и j и переместить короткую перегородку внутрь на одну позицию.
  4. Циклически выполнять шаги 2. и 3., пока i и j не встретятся.

=== "<1>" Жадный процесс решения задачи о максимальной вместимости

=== "<2>" max_capacity_greedy_step2

=== "<3>" max_capacity_greedy_step3

=== "<4>" max_capacity_greedy_step4

=== "<5>" max_capacity_greedy_step5

=== "<6>" max_capacity_greedy_step6

=== "<7>" max_capacity_greedy_step7

=== "<8>" max_capacity_greedy_step8

=== "<9>" max_capacity_greedy_step9

Реализация кода

Код выполняет не более n итераций, поэтому временная сложность составляет $O(n)$.

Переменные i, j, res используют константное дополнительное пространство, поэтому пространственная сложность составляет $O(1)$.

[file]{max_capacity}-[class]{}-[func]{max_capacity}

Доказательство корректности

Причина, по которой жадный алгоритм быстрее полного перебора, заключается в том, что каждый жадный выбор «пропускает» некоторые состояния.

Например, в состоянии cap[i, j], где i — короткая перегородка, j — длинная перегородка. Если жадно переместить короткую перегородку i внутрь на одну позицию, это приведет к «пропуску» состояний, показанных на рисунке ниже. Это означает, что в дальнейшем невозможно будет проверить вместимость этих состояний.


cap[i, i+1], cap[i, i+2], \dots, cap[i, j-2], cap[i, j-1]

Пропущенные состояния при перемещении короткой перегородки

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

Приведенный выше анализ показывает, что операция перемещения короткой перегородки является «безопасной», жадная стратегия эффективна.