Files
hello-algo/ru/docs/chapter_greedy/max_capacity_problem.md
T
krahets e53a7f2498 build
2026-04-14 18:06:14 +08:00

30 KiB

comments
comments
true

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

!!! question

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

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

Требуется выбрать две перегородки так, чтобы образованный ими контейнер имел максимальную вместимость. Пример показан на рисунке 15-7.

Пример данных для задачи о максимальной вместимости{ class="animation-figure" }

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

Контейнер образуется произвольными двумя перегородками, поэтому состоянием задачи служит пара индексов этих перегородок, обозначим ее как $[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).

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

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

Начальное состояние{ class="animation-figure" }

Рисунок 15-8   Начальное состояние

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

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

Состояние после перемещения длинной перегородки внутрь{ class="animation-figure" }

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

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

Состояние после перемещения короткой перегородки внутрь{ class="animation-figure" }

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

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

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

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

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

=== "<2>" max_capacity_greedy_step2{ class="animation-figure" }

=== "<3>" max_capacity_greedy_step3{ class="animation-figure" }

=== "<4>" max_capacity_greedy_step4{ class="animation-figure" }

=== "<5>" max_capacity_greedy_step5{ class="animation-figure" }

=== "<6>" max_capacity_greedy_step6{ class="animation-figure" }

=== "<7>" max_capacity_greedy_step7{ class="animation-figure" }

=== "<8>" max_capacity_greedy_step8{ class="animation-figure" }

=== "<9>" max_capacity_greedy_step9{ class="animation-figure" }

Рисунок 15-11   Жадный процесс решения задачи о максимальной вместимости

2.   Код реализации

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

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

=== "Python"

```python title="max_capacity.py"
def max_capacity(ht: list[int]) -> int:
    """Максимальная вместимость: жадный алгоритм"""
    # Инициализировать i и j так, чтобы они располагались по двум концам массива
    i, j = 0, len(ht) - 1
    # Начальная максимальная вместимость равна 0
    res = 0
    # Выполнять жадный выбор в цикле, пока две доски не встретятся
    while i < j:
        # Обновить максимальную вместимость
        cap = min(ht[i], ht[j]) * (j - i)
        res = max(res, cap)
        # Сдвигать внутрь более короткую сторону
        if ht[i] < ht[j]:
            i += 1
        else:
            j -= 1
    return res
```

=== "C++"

```cpp title="max_capacity.cpp"
/* Максимальная вместимость: жадный алгоритм */
int maxCapacity(vector<int> &ht) {
    // Инициализировать i и j так, чтобы они располагались по двум концам массива
    int i = 0, j = ht.size() - 1;
    // Начальная максимальная вместимость равна 0
    int res = 0;
    // Выполнять жадный выбор в цикле, пока две доски не встретятся
    while (i < j) {
        // Обновить максимальную вместимость
        int cap = min(ht[i], ht[j]) * (j - i);
        res = max(res, cap);
        // Сдвигать внутрь более короткую сторону
        if (ht[i] < ht[j]) {
            i++;
        } else {
            j--;
        }
    }
    return res;
}
```

=== "Java"

```java title="max_capacity.java"
/* Максимальная вместимость: жадный алгоритм */
int maxCapacity(int[] ht) {
    // Инициализировать i и j так, чтобы они располагались по двум концам массива
    int i = 0, j = ht.length - 1;
    // Начальная максимальная вместимость равна 0
    int res = 0;
    // Выполнять жадный выбор в цикле, пока две доски не встретятся
    while (i < j) {
        // Обновить максимальную вместимость
        int cap = Math.min(ht[i], ht[j]) * (j - i);
        res = Math.max(res, cap);
        // Сдвигать внутрь более короткую сторону
        if (ht[i] < ht[j]) {
            i++;
        } else {
            j--;
        }
    }
    return res;
}
```

=== "C#"

```csharp title="max_capacity.cs"
/* Максимальная вместимость: жадный алгоритм */
int MaxCapacity(int[] ht) {
    // Инициализировать i и j так, чтобы они располагались по двум концам массива
    int i = 0, j = ht.Length - 1;
    // Начальная максимальная вместимость равна 0
    int res = 0;
    // Выполнять жадный выбор в цикле, пока две доски не встретятся
    while (i < j) {
        // Обновить максимальную вместимость
        int cap = Math.Min(ht[i], ht[j]) * (j - i);
        res = Math.Max(res, cap);
        // Сдвигать внутрь более короткую сторону
        if (ht[i] < ht[j]) {
            i++;
        } else {
            j--;
        }
    }
    return res;
}
```

=== "Go"

```go title="max_capacity.go"
/* Максимальная вместимость: жадный алгоритм */
func maxCapacity(ht []int) int {
    // Инициализировать i и j так, чтобы они располагались по двум концам массива
    i, j := 0, len(ht)-1
    // Начальная максимальная вместимость равна 0
    res := 0
    // Выполнять жадный выбор в цикле, пока две доски не встретятся
    for i < j {
        // Обновить максимальную вместимость
        capacity := int(math.Min(float64(ht[i]), float64(ht[j]))) * (j - i)
        res = int(math.Max(float64(res), float64(capacity)))
        // Сдвигать внутрь более короткую сторону
        if ht[i] < ht[j] {
            i++
        } else {
            j--
        }
    }
    return res
}
```

=== "Swift"

```swift title="max_capacity.swift"
/* Максимальная вместимость: жадный алгоритм */
func maxCapacity(ht: [Int]) -> Int {
    // Инициализировать i и j так, чтобы они располагались по двум концам массива
    var i = ht.startIndex, j = ht.endIndex - 1
    // Начальная максимальная вместимость равна 0
    var res = 0
    // Выполнять жадный выбор в цикле, пока две доски не встретятся
    while i < j {
        // Обновить максимальную вместимость
        let cap = min(ht[i], ht[j]) * (j - i)
        res = max(res, cap)
        // Сдвигать внутрь более короткую сторону
        if ht[i] < ht[j] {
            i += 1
        } else {
            j -= 1
        }
    }
    return res
}
```

=== "JS"

```javascript title="max_capacity.js"
/* Максимальная вместимость: жадный алгоритм */
function maxCapacity(ht) {
    // Инициализировать i и j так, чтобы они располагались по двум концам массива
    let i = 0,
        j = ht.length - 1;
    // Начальная максимальная вместимость равна 0
    let res = 0;
    // Выполнять жадный выбор в цикле, пока две доски не встретятся
    while (i < j) {
        // Обновить максимальную вместимость
        const cap = Math.min(ht[i], ht[j]) * (j - i);
        res = Math.max(res, cap);
        // Сдвигать внутрь более короткую сторону
        if (ht[i] < ht[j]) {
            i += 1;
        } else {
            j -= 1;
        }
    }
    return res;
}
```

=== "TS"

```typescript title="max_capacity.ts"
/* Максимальная вместимость: жадный алгоритм */
function maxCapacity(ht: number[]): number {
    // Инициализировать i и j так, чтобы они располагались по двум концам массива
    let i = 0,
        j = ht.length - 1;
    // Начальная максимальная вместимость равна 0
    let res = 0;
    // Выполнять жадный выбор в цикле, пока две доски не встретятся
    while (i < j) {
        // Обновить максимальную вместимость
        const cap: number = Math.min(ht[i], ht[j]) * (j - i);
        res = Math.max(res, cap);
        // Сдвигать внутрь более короткую сторону
        if (ht[i] < ht[j]) {
            i += 1;
        } else {
            j -= 1;
        }
    }
    return res;
}
```

=== "Dart"

```dart title="max_capacity.dart"
/* Максимальная вместимость: жадный алгоритм */
int maxCapacity(List<int> ht) {
  // Инициализировать i и j так, чтобы они располагались по двум концам массива
  int i = 0, j = ht.length - 1;
  // Начальная максимальная вместимость равна 0
  int res = 0;
  // Выполнять жадный выбор в цикле, пока две доски не встретятся
  while (i < j) {
    // Обновить максимальную вместимость
    int cap = min(ht[i], ht[j]) * (j - i);
    res = max(res, cap);
    // Сдвигать внутрь более короткую сторону
    if (ht[i] < ht[j]) {
      i++;
    } else {
      j--;
    }
  }
  return res;
}
```

=== "Rust"

```rust title="max_capacity.rs"
/* Максимальная вместимость: жадный алгоритм */
fn max_capacity(ht: &[i32]) -> i32 {
    // Инициализировать i и j так, чтобы они располагались по двум концам массива
    let mut i = 0;
    let mut j = ht.len() - 1;
    // Начальная максимальная вместимость равна 0
    let mut res = 0;
    // Выполнять жадный выбор в цикле, пока две доски не встретятся
    while i < j {
        // Обновить максимальную вместимость
        let cap = std::cmp::min(ht[i], ht[j]) * (j - i) as i32;
        res = std::cmp::max(res, cap);
        // Сдвигать внутрь более короткую сторону
        if ht[i] < ht[j] {
            i += 1;
        } else {
            j -= 1;
        }
    }
    res
}
```

=== "C"

```c title="max_capacity.c"
/* Максимальная вместимость: жадный алгоритм */
int maxCapacity(int ht[], int htLength) {
    // Инициализировать i и j так, чтобы они располагались по двум концам массива
    int i = 0;
    int j = htLength - 1;
    // Начальная максимальная вместимость равна 0
    int res = 0;
    // Выполнять жадный выбор в цикле, пока две доски не встретятся
    while (i < j) {
        // Обновить максимальную вместимость
        int capacity = myMin(ht[i], ht[j]) * (j - i);
        res = myMax(res, capacity);
        // Сдвигать внутрь более короткую сторону
        if (ht[i] < ht[j]) {
            i++;
        } else {
            j--;
        }
    }
    return res;
}
```

=== "Kotlin"

```kotlin title="max_capacity.kt"
/* Максимальная вместимость: жадный алгоритм */
fun maxCapacity(ht: IntArray): Int {
    // Инициализировать i и j так, чтобы они располагались по двум концам массива
    var i = 0
    var j = ht.size - 1
    // Начальная максимальная вместимость равна 0
    var res = 0
    // Выполнять жадный выбор в цикле, пока две доски не встретятся
    while (i < j) {
        // Обновить максимальную вместимость
        val cap = min(ht[i], ht[j]) * (j - i)
        res = max(res, cap)
        // Сдвигать внутрь более короткую сторону
        if (ht[i] < ht[j]) {
            i++
        } else {
            j--
        }
    }
    return res
}
```

=== "Ruby"

```ruby title="max_capacity.rb"
### Максимальная вместимость: жадный алгоритм ###
def max_capacity(ht)
  # Инициализировать i и j так, чтобы они располагались по двум концам массива
  i, j = 0, ht.length - 1
  # Начальная максимальная вместимость равна 0
  res = 0

  # Выполнять жадный выбор в цикле, пока две доски не встретятся
  while i < j
    # Обновить максимальную вместимость
    cap = [ht[i], ht[j]].min * (j - i)
    res = [res, cap].max
    # Сдвигать внутрь более короткую сторону
    if ht[i] < ht[j]
      i += 1
    else
      j -= 1
    end
  end

  res
end
```

??? pythontutor "Визуализация кода"

<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20max_capacity%28ht%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%3A%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%22%22%22%0A%20%20%20%20%23%20%D0%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20i%20%D0%B8%20j%20%D1%82%D0%B0%D0%BA%2C%20%D1%87%D1%82%D0%BE%D0%B1%D1%8B%20%D0%BE%D0%BD%D0%B8%20%D1%80%D0%B0%D1%81%D0%BF%D0%BE%D0%BB%D0%B0%D0%B3%D0%B0%D0%BB%D0%B8%D1%81%D1%8C%20%D0%BF%D0%BE%20%D0%B4%D0%B2%D1%83%D0%BC%20%D0%BA%D0%BE%D0%BD%D1%86%D0%B0%D0%BC%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%0A%20%20%20%20i%2C%20j%20%3D%200%2C%20len%28ht%29%20-%201%0A%20%20%20%20%23%20%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%D1%80%D0%B0%D0%B2%D0%BD%D0%B0%200%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20%23%20%D0%92%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D1%8F%D1%82%D1%8C%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%20%D0%B2%20%D1%86%D0%B8%D0%BA%D0%BB%D0%B5%2C%20%D0%BF%D0%BE%D0%BA%D0%B0%20%D0%B4%D0%B2%D0%B5%20%D0%B4%D0%BE%D1%81%D0%BA%D0%B8%20%D0%BD%D0%B5%20%D0%B2%D1%81%D1%82%D1%80%D0%B5%D1%82%D1%8F%D1%82%D1%81%D1%8F%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%9E%D0%B1%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%83%D1%8E%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%0A%20%20%20%20%20%20%20%20cap%20%3D%20min%28ht%5Bi%5D%2C%20ht%5Bj%5D%29%20%2A%20%28j%20-%20i%29%0A%20%20%20%20%20%20%20%20res%20%3D%20max%28res%2C%20cap%29%0A%20%20%20%20%20%20%20%20%23%20%D0%A1%D0%B4%D0%B2%D0%B8%D0%B3%D0%B0%D1%82%D1%8C%20%D0%B2%D0%BD%D1%83%D1%82%D1%80%D1%8C%20%D0%B1%D0%BE%D0%BB%D0%B5%D0%B5%20%D0%BA%D0%BE%D1%80%D0%BE%D1%82%D0%BA%D1%83%D1%8E%20%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D1%83%0A%20%20%20%20%20%20%20%20if%20ht%5Bi%5D%20%3C%20ht%5Bj%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20ht%20%3D%20%5B3%2C%208%2C%205%2C%202%2C%207%2C%207%2C%203%2C%204%5D%0A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%0A%20%20%20%20res%20%3D%20max_capacity%28ht%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%3D%20%7Bres%7D%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20max_capacity%28ht%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%3A%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%22%22%22%0A%20%20%20%20%23%20%D0%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20i%20%D0%B8%20j%20%D1%82%D0%B0%D0%BA%2C%20%D1%87%D1%82%D0%BE%D0%B1%D1%8B%20%D0%BE%D0%BD%D0%B8%20%D1%80%D0%B0%D1%81%D0%BF%D0%BE%D0%BB%D0%B0%D0%B3%D0%B0%D0%BB%D0%B8%D1%81%D1%8C%20%D0%BF%D0%BE%20%D0%B4%D0%B2%D1%83%D0%BC%20%D0%BA%D0%BE%D0%BD%D1%86%D0%B0%D0%BC%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%0A%20%20%20%20i%2C%20j%20%3D%200%2C%20len%28ht%29%20-%201%0A%20%20%20%20%23%20%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%D1%80%D0%B0%D0%B2%D0%BD%D0%B0%200%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20%23%20%D0%92%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D1%8F%D1%82%D1%8C%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%20%D0%B2%20%D1%86%D0%B8%D0%BA%D0%BB%D0%B5%2C%20%D0%BF%D0%BE%D0%BA%D0%B0%20%D0%B4%D0%B2%D0%B5%20%D0%B4%D0%BE%D1%81%D0%BA%D0%B8%20%D0%BD%D0%B5%20%D0%B2%D1%81%D1%82%D1%80%D0%B5%D1%82%D1%8F%D1%82%D1%81%D1%8F%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%9E%D0%B1%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%83%D1%8E%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%0A%20%20%20%20%20%20%20%20cap%20%3D%20min%28ht%5Bi%5D%2C%20ht%5Bj%5D%29%20%2A%20%28j%20-%20i%29%0A%20%20%20%20%20%20%20%20res%20%3D%20max%28res%2C%20cap%29%0A%20%20%20%20%20%20%20%20%23%20%D0%A1%D0%B4%D0%B2%D0%B8%D0%B3%D0%B0%D1%82%D1%8C%20%D0%B2%D0%BD%D1%83%D1%82%D1%80%D1%8C%20%D0%B1%D0%BE%D0%BB%D0%B5%D0%B5%20%D0%BA%D0%BE%D1%80%D0%BE%D1%82%D0%BA%D1%83%D1%8E%20%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D1%83%0A%20%20%20%20%20%20%20%20if%20ht%5Bi%5D%20%3C%20ht%5Bj%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20ht%20%3D%20%5B3%2C%208%2C%205%2C%202%2C%207%2C%207%2C%203%2C%204%5D%0A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%0A%20%20%20%20res%20%3D%20max_capacity%28ht%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%3D%20%7Bres%7D%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div>

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

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

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


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

Состояния, пропущенные из-за смещения короткой перегородки{ class="animation-figure" }

Рисунок 15-12   Состояния, пропущенные из-за смещения короткой перегородки

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

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