mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-08 21:46:06 +00:00
99 lines
9.1 KiB
Markdown
99 lines
9.1 KiB
Markdown
# Задача о максимальной вместимости
|
|
|
|
!!! 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>"
|
|

|
|
|
|
=== "<3>"
|
|

|
|
|
|
=== "<4>"
|
|

|
|
|
|
=== "<5>"
|
|

|
|
|
|
=== "<6>"
|
|

|
|
|
|
=== "<7>"
|
|

|
|
|
|
=== "<8>"
|
|

|
|
|
|
=== "<9>"
|
|

|
|
|
|
### Реализация кода
|
|
|
|
Код выполняет не более $n$ итераций, **поэтому временная сложность составляет $O(n)$**.
|
|
|
|
Переменные $i$, $j$, $res$ используют константное дополнительное пространство, **поэтому пространственная сложность составляет $O(1)$**.
|
|
|
|
```src
|
|
[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$ внутрь**. Ранее мы уже доказали, что перемещение длинной перегородки внутрь обязательно приводит к уменьшению вместимости. То есть пропущенные состояния не могут быть оптимальным решением, **их пропуск не приведет к потере оптимального решения**.
|
|
|
|
Приведенный выше анализ показывает, что операция перемещения короткой перегородки является «безопасной», жадная стратегия эффективна. |