First version.

This commit is contained in:
krahets
2026-01-20 15:08:42 +08:00
parent 2213a59ff6
commit 8071daddaa
106 changed files with 11790 additions and 0 deletions
@@ -0,0 +1,52 @@
# Задача о дробном рюкзаке
!!! question
Дано *n* предметов, где *i*-й предмет имеет массу *wgt*[*i*-1] и стоимость *val*[*i*-1], и рюкзак вместимостью *cap*. Каждый предмет можно выбрать только один раз, **но можно выбрать часть предмета, при этом стоимость рассчитывается пропорционально выбранной массе**. Требуется найти максимальную стоимость предметов в рюкзаке при ограниченной вместимости. Пример показан на рисунке ниже.
![Пример данных для задачи о дробном рюкзаке](../assets/media/image1036.jpeg)
Задача о дробном рюкзаке и задача о рюкзаке 0-1 в целом очень похожи: состояние включает текущий предмет *i* и вместимость *c*, цель -- найти максимальную стоимость при ограниченной вместимости рюкзака.
Отличие в том, что в данной задаче допускается выбирать часть предмета. Как показано на рисунке ниже, **можно произвольно разделять предметы и рассчитывать соответствующую стоимость пропорционально массе**.
1. Для предмета *i* его стоимость на единицу массы равна *val*[*i* - 1]/*wgt*[*i* - 1], сокращенно -- удельная стоимость.
2. Предположим, что в рюкзак помещена часть предмета *i* массой *w*, тогда увеличение стоимости рюкзака составит *w* × *val*[*i* - 1]/*wgt*[*i* - 1].
![Стоимость предметов на единицу массы](../assets/media/image1040.jpeg)
### Определение жадной стратегии
Максимизация общей стоимости предметов в рюкзаке, по сути, является максимизацией стоимости предметов на единицу массы. Из этого можно вывести жадную стратегию, изображенную на рисунке ниже.
1. Отсортировать предметы по убыванию стоимости на единицу массы.
2. Перебирать все предметы и **жадно выбирать на каждом этапе предмет с наивысшей стоимостью на единицу массы**.
3. Если оставшейся вместимости рюкзака недостаточно, использовать часть текущего предмета для заполнения рюкзака.
![Жадная стратегия для задачи о дробном рюкзаке](../assets/media/image1042.jpeg)
### Реализация кода
Мы создали класс предмета `Item` для сортировки предметов по стоимости на единицу массы. Выполняем цикл жадного выбора, прерываем его и возвращаем решение, когда рюкзак заполнен:
```src
[file]{fractional_knapsack}-[class]{}-[func]{fractional_knapsack}
```
Временная сложность встроенных алгоритмов сортировки обычно составляет *O*(*n* log *n*), пространственная сложность обычно *O*(log *n*) или *O*(*n*), в зависимости от конкретной реализации в языке программирования.
Помимо сортировки, в худшем случае необходимо перебрать весь список предметов, **поэтому временная сложность составляет *O*(*n*)**, где *n* -- количество предметов.
Поскольку инициализируется список объектов `Item`, **пространственная сложность составляет *O*(*n*)**.
### Доказательство корректности
Используем метод доказательства от противного. Предположим, что предмет *x* имеет наивысшую стоимость на единицу массы, и некоторый алгоритм находит максимальную стоимость `res`, но это решение не содержит предмет *x*.
Теперь извлечем из рюкзака единицу массы любого предмета и заменим ее единицей массы предмета *x*. Поскольку предмет *x* имеет наивысшую стоимость на единицу массы, общая стоимость после замены обязательно будет больше `res`. **Это противоречит тому, что `res` является оптимальным решением, следовательно, оптимальное решение обязательно должно содержать предмет *x***.
Для других предметов в этом решении мы также можем построить подобное противоречие. В общем, **предметы с большей стоимостью на единицу массы всегда являются более оптимальным выбором**, что доказывает эффективность жадной стратегии.
Как показано на рисунке ниже, если рассматривать массу предмета и стоимость предмета на единицу массы как горизонтальную и вертикальную оси двумерного графика соответственно, то задача о дробном рюкзаке может быть преобразована в «нахождение максимальной площади в ограниченном интервале горизонтальной оси». Эта аналогия может помочь понять эффективность жадной стратегии с геометрической точки зрения.
![Геометрическое представление задачи о дробном рюкзаке](../assets/media/image1042.jpeg)
@@ -0,0 +1,91 @@
# Жадные алгоритмы
<u>Жадный алгоритм</u> -- это распространенный метод решения задач оптимизации. Его основная идея заключается в том, чтобы на каждом этапе принятия решения выбирать наиболее оптимальный на данный момент вариант, т. е. с жадностью принимать локально оптимальные решения в надежде получить глобально оптимальное решение. Жадные алгоритмы просты и эффективны, и они находят широкое применение в решении многих практических задач.
Жадные алгоритмы и динамическое программирование часто используются для решения задач оптимизации. Между ними есть некоторые сходства, например оба метода зависят от свойств оптимальной подструктуры, но их принципы работы различны.
- Динамическое программирование для получения текущего решения учитывает все предыдущие решения и использует решения предыдущих подзадач для построения решения текущей подзадачи.
- Жадный алгоритм не учитывает предыдущие решения, а просто движется вперед, делая жадные выборы и постепенно сокращая область задачи, пока она не будет решена.
Чтобы лучше понять принцип работы жадного алгоритма, рассмотрим его применение к задаче о размене монет. Она уже была рассмотрена в разделе «Задача о полном рюкзаке», и, вероятно, вы с ней уже знакомы.
!!! question
Дано *n* видов монет, номинал *i*-й монеты равен *coins*[*i* - 1], целевая сумма *amt*, каждый вид монет можно выбирать многократно. Найти минимальное количество монет, необходимое для получения целевой суммы. Если получить целевую сумму невозможно, вернуть -1.
Жадная стратегия, применяемая в этой задаче, показана на следующем рисунке. Для заданной целевой суммы **мы жадно выбираем монету, которая не превышает и наиболее близка к этой сумме**, и повторяем этот шаг, пока не будет достигнута целевая сумма.
![Жадная стратегия для задачи о размене монет](../assets/media/image1030.jpeg)
Ниже приведен код реализации.
```src
[file]{coin_change_greedy}-[class]{}-[func]{coin_change_greedy}
```
Вы можете невольно воскликнуть: «Эврика!» Жадный алгоритм решает задачу размена монет всего за десяток строк кода.
## Преимущества и ограничения жадных алгоритмов
**Жадные алгоритмы не только просты в реализации, но и обычно очень эффективны**. Если в приведенном выше коде обозначить минимальный номинал монеты как *min*(*coins*), то жадный выбор выполняется не более *amt* / *min*(*coins*) раз. Тогда временная сложность составляет *O*(*amt* / *min*(*coins*)). Это на порядок меньше временной сложности решения с использованием динамического программирования *O*(*n* × *amt*).
Однако **для некоторых комбинаций номиналов монет жадный алгоритм не сможет найти оптимальное решение**. На следующем рисунке приведены два примера.
- **Положительный пример** *coins* = [1, 5, 10, 20, 50, 100]: при данной комбинации монет для любого *amt* жадный алгоритм сможет найти оптимальное решение.
- **Отрицательный пример** *coins* = [1, 20, 50]: если *amt* = 60, жадный алгоритм найдет комбинацию 50 + 1 × 10, всего 11 монет. Но динамическое программирование может найти оптимальное решение 20 + 20 + 20, всего 3 монеты.
- **Отрицательный пример** *coins* = [1, 49, 50]: если *amt* = 98, жадный алгоритм найдет комбинацию 50 + 1 × 48, всего 49 монет. Но динамическое программирование может найти оптимальное решение 49 + 49, всего 2 монеты.
![Примеры, когда жадный алгоритм не может найти оптимальное решение](../assets/media/image1032.jpeg)
Таким образом, для задачи размена монет жадный алгоритм не гарантирует нахождение глобально оптимального решения и может привести к очень плохому решению. Для решения этой задачи лучше подходит динамическое программирование.
В общем случае жадные алгоритмы применимы в следующих двух ситуациях:
1. **можно гарантировать нахождение оптимального решения**: в этом случае жадный алгоритм часто является лучшим выбором, так как он обычно более эффективен, чем методы обратного поиска и динамического программирования;
2. **можно найти приближенное оптимальное решение**: в этом случае жадный алгоритм также применим. Для многих сложных задач поиск глобально оптимального решения очень затруднителен, и возможность найти субоптимальное решение с высокой эффективностью является весьма хорошим результатом.
## Свойства жадных алгоритмов
Итак, возникает вопрос: какие задачи подходят для решения с помощью жадного алгоритма? Или, иначе говоря, в каких случаях жадный алгоритм может гарантировать нахождение оптимального решения?
По сравнению с динамическим программированием условия применения жадного алгоритма более строгие, и они в основном сосредоточены на двух свойствах задачи.
- **Свойство жадного выбора**: жадный алгоритм может гарантировать получение оптимального решения только в случае, если локально оптимальный выбор всегда приводит к глобально оптимальному решению.
- **Оптимальная подструктура**: оптимальное решение исходной задачи содержит оптимальное решение подзадачи.
Оптимальная подструктура уже была рассмотрена в главе «Динамическое программирование», поэтому здесь не будем повторяться. Стоит отметить, что оптимальная подструктура некоторых задач не всегда очевидна, но их все же можно решить с помощью жадного алгоритма.
Основное внимание уделяется методам определения свойства жадного выбора. Хотя его описание кажется простым, **на практике доказательство этого свойства для многих задач является сложной задачей**.
Например, в задаче о размене монет мы можем легко привести контрпример для опровержения свойства жадного выбора. Однако доказательство его истинности значительно сложнее. На вопрос «**При каких условиях можно использовать жадный алгоритм для решения задачи размена монет**?» обычно мы можем дать лишь интуитивный или примерный ответ, но не можем предоставить строгое математическое доказательство.
!!! quote
Существует статья, в которой представлен алгоритм временной сложности *O*(*n*³) для определения того, можно ли использовать жадный алгоритм для нахождения оптимального решения для любой суммы при данной комбинации монет.
Pearson, D. A polynomial-time algorithm for the change-making problem[J]. Operations Research Letters, 2005, 33(3): 231-234.
## Этапы решения задач жадным алгоритмом
Процесс решения жадных задач можно разделить на следующие три этапа:
1. **анализ задачи**: изучение и понимание характеристик задачи, включая определение состояния, цели оптимизации и ограничения. Этот этап также присутствует в методах поиска с возвратом и динамического программирования;
2. **определение жадной стратегии**: определение того, как делать жадный выбор на каждом шаге. Эта стратегия позволяет уменьшать размер задачи на каждом шаге и в конечном итоге решить всю задачу;
3. **доказательство корректности**: обычно требуется доказать наличие свойства жадного выбора и оптимальной подструктуры задачи. Этот этап может потребовать использования математических доказательств, таких как метод математической индукции или доказательство от противного.
Определение жадной стратегии является ключевым этапом решения задачи, но его реализация может быть непростой по следующим причинам.
- **Жадные стратегии для различных задач могут значительно различаться**. Для многих задач жадная стратегия очевидна, и ее можно определить с помощью общего размышления и эмпирических проб. Однако для некоторых сложных задач жадная стратегия может оказаться очень скрытой, что потребует значительного опыта в решении задач и навыков работы с алгоритмами.
- **Некоторые жадные стратегии могут быть обманчивыми**. Бывает, жадная стратегия разработана с полной уверенностью в ее правильности, код написан и отправлен на выполнение. Но оказывается, что некоторые тестовые примеры не проходят проверку на корректность. Это происходит потому, что разработанная жадная стратегия является лишь частично правильной, как в случае с задачей о размене монет, описанной выше.
Для обеспечения корректности необходимо провести строгое математическое доказательство жадной стратегии, **обычно с использованием метода доказательства от противного или метода математической индукции**.
Тем не менее доказательство корректности может оказаться непростой задачей. Если нет ясности, обычно выбирается отладка кода на основе тестовых примеров с постепенной модификацией и проверкой жадной стратегии.
## Типичные задачи для жадного алгоритма
Жадный алгоритм часто применяется в задачах оптимизации, удовлетворяющих свойству жадного выбора и оптимальной подструктуре. Ниже перечислены некоторые типичные задачи для жадного алгоритма.
- **Задача о размене монет**: при некоторых комбинациях монет жадный алгоритм всегда может получить оптимальное решение.
- **Задача о расписании интервалов**: пусть у вас есть несколько задач, каждая из которых выполняется в течение определенного времени, и ваша цель -- выполнить как можно больше задач. Если каждый раз выбирать задачу с наименьшим временем окончания, то жадный алгоритм может дать оптимальное решение.
- **Задача о дробном рюкзаке**: дана группа предметов и вместимость. Ваша цель -- выбрать группу предметов так, чтобы
+9
View File
@@ -0,0 +1,9 @@
# Жадность
![Жадность](../assets/covers/chapter_greedy.jpg)
!!! abstract
Подсолнух поворачивается к солнцу, постоянно стремясь к максимальному росту.
Жадная стратегия через серию простых выборов постепенно приводит к наилучшему ответу.
@@ -0,0 +1,99 @@
# Задача о максимальной вместимости
!!! question
Дан массив $ht$, каждый элемент которого представляет высоту вертикальной перегородки. Любые две перегородки в массиве и пространство между ними могут образовать контейнер.
Вместимость контейнера равна произведению высоты и ширины (площади), где высота определяется более короткой перегородкой, а ширина — разностью индексов двух перегородок в массиве.
Необходимо выбрать в массиве две перегородки так, чтобы вместимость образованного контейнера была максимальной, и вернуть максимальную вместимость. Пример показан на рисунке ниже.
![Пример данных для задачи о максимальной вместимости](../assets/max_capacity_example.png)
Контейнер образован любыми двумя перегородками, **поэтому состоянием в данной задаче являются индексы двух перегородок, обозначим их как $[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$ — длинная перегородка.
![Начальное состояние](../assets/max_capacity_initial_state.png)
Как показано на рисунке ниже, **если сейчас переместить длинную перегородку $j$ ближе к короткой перегородке $i$, вместимость обязательно уменьшится**.
Это происходит потому, что после перемещения длинной перегородки $j$ ширина $j-i$ определенно уменьшится; а высота определяется короткой перегородкой, поэтому высота может только остаться неизменной ($i$ остается короткой перегородкой) или уменьшиться (перемещенная $j$ становится короткой перегородкой).
![Состояние после перемещения длинной перегородки внутрь](../assets/max_capacity_moving_long_board.png)
Рассуждая в обратном направлении, **только сжимая короткую перегородку $i$ внутрь, мы можем увеличить вместимость**. Потому что хотя ширина определенно уменьшится, **высота может увеличиться** (перемещенная короткая перегородка $i$ может стать длиннее). Например, на рисунке ниже после перемещения короткой перегородки площадь увеличивается.
![Состояние после перемещения короткой перегородки внутрь](../assets/max_capacity_moving_short_board.png)
Таким образом, можно вывести жадную стратегию для данной задачи: инициализировать два указателя по краям контейнера, на каждом шаге сжимать внутрь указатель, соответствующий короткой перегородке, пока два указателя не встретятся.
На рисунке ниже показан процесс выполнения жадной стратегии.
1. В начальном состоянии указатели $i$ и $j$ находятся на противоположных концах массива.
2. Вычислить вместимость текущего состояния $cap[i, j]$ и обновить максимальную вместимость.
3. Сравнить высоты перегородок $i$ и $j$ и переместить короткую перегородку внутрь на одну позицию.
4. Циклически выполнять шаги `2.` и `3.`, пока $i$ и $j$ не встретятся.
=== "<1>"
![Жадный процесс решения задачи о максимальной вместимости](../assets/max_capacity_greedy_step1.png)
=== "<2>"
![max_capacity_greedy_step2](../assets/max_capacity_greedy_step2.png)
=== "<3>"
![max_capacity_greedy_step3](../assets/max_capacity_greedy_step3.png)
=== "<4>"
![max_capacity_greedy_step4](../assets/max_capacity_greedy_step4.png)
=== "<5>"
![max_capacity_greedy_step5](../assets/max_capacity_greedy_step5.png)
=== "<6>"
![max_capacity_greedy_step6](../assets/max_capacity_greedy_step6.png)
=== "<7>"
![max_capacity_greedy_step7](../assets/max_capacity_greedy_step7.png)
=== "<8>"
![max_capacity_greedy_step8](../assets/max_capacity_greedy_step8.png)
=== "<9>"
![max_capacity_greedy_step9](../assets/max_capacity_greedy_step9.png)
### Реализация кода
Код выполняет не более $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]
$$
![Пропущенные состояния при перемещении короткой перегородки](../assets/max_capacity_skipped_states.png)
При наблюдении обнаруживается, что **эти пропущенные состояния фактически являются всеми состояниями при перемещении длинной перегородки $j$ внутрь**. Ранее мы уже доказали, что перемещение длинной перегородки внутрь обязательно приводит к уменьшению вместимости. То есть пропущенные состояния не могут быть оптимальным решением, **их пропуск не приведет к потере оптимального решения**.
Приведенный выше анализ показывает, что операция перемещения короткой перегородки является «безопасной», жадная стратегия эффективна.
@@ -0,0 +1,85 @@
# Задача о максимальном произведении при разбиении
!!! question
Дано положительное целое число $n$, которое нужно разбить на сумму как минимум двух положительных целых чисел. Найдите максимальное произведение всех целых чисел после разбиения, как показано на рисунке ниже.
![Определение задачи о максимальном произведении при разбиении](../assets/max_product_cutting_definition.png)
Предположим, что мы разбиваем $n$ на $m$ целочисленных множителей, где $i$-й множитель обозначается как $n_i$, то есть
$$
n = \sum_{i=1}^{m}n_i
$$
Цель данной задачи -- найти максимальное произведение всех целочисленных множителей, то есть
$$
\max(\prod_{i=1}^{m}n_i)
$$
Нам нужно подумать: каким должно быть количество разбиений $m$ и каким должен быть каждый $n_i$?
### Определение жадной стратегии
Исходя из опыта, произведение двух целых чисел часто больше их суммы. Предположим, что из $n$ выделяется множитель $2$, тогда их произведение равно $2(n-2)$. Сравним это произведение с $n$:
$$
\begin{aligned}
2(n-2) & \geq n \newline
2n - n - 4 & \geq 0 \newline
n & \geq 4
\end{aligned}
$$
Как показано на рисунке ниже, когда $n \geq 4$, выделение $2$ увеличивает произведение, **это означает, что целые числа больше или равные $4$ должны быть разбиты**.
**Жадная стратегия один**: Если в схеме разбиения содержится множитель $\geq 4$, то он должен быть разбит дальше. В итоговой схеме разбиения должны появляться только три множителя: $1$, $2$, $3$.
![Разбиение приводит к увеличению произведения](../assets/max_product_cutting_greedy_infer1.png)
Далее подумаем, какой множитель является оптимальным. Среди трех множителей $1$, $2$, $3$ очевидно, что $1$ -- наихудший, потому что $1 \times (n-1) < n$ всегда верно, то есть выделение $1$ фактически приводит к уменьшению произведения.
Как показано на рисунке ниже, когда $n = 6$, имеем $3 \times 3 > 2 \times 2 \times 2$. **Это означает, что выделение $3$ лучше, чем выделение $2$**.
**Жадная стратегия два**: В схеме разбиения должно быть не более двух $2$. Потому что три $2$ всегда можно заменить на два $3$, получив таким образом большее произведение.
![Оптимальный множитель разбиения](../assets/max_product_cutting_greedy_infer2.png)
Исходя из вышесказанного, можно вывести следующую жадную стратегию:
1. Для входного целого числа $n$ постоянно выделяем из него множитель $3$, пока остаток не станет равным $0$, $1$ или $2$.
2. Когда остаток равен $0$, это означает, что $n$ кратно $3$, поэтому никаких действий не требуется.
3. Когда остаток равен $2$, не продолжаем разбиение, сохраняем его.
4. Когда остаток равен $1$, поскольку $2 \times 2 > 1 \times 3$, следует заменить последнюю $3$ на $2$.
### Реализация кода
Как показано на рисунке ниже, нам не нужно использовать цикл для разбиения целого числа, а можно использовать операцию целочисленного деления для получения количества $3$ -- $a$, и операцию взятия остатка для получения остатка $b$, при этом имеем:
$$
n = 3 a + b
$$
Обратите внимание, что для граничного случая $n \leq 3$ необходимо выделить $1$, произведение равно $1 \times (n - 1)$.
```src
[file]{max_product_cutting}-[class]{}-[func]{max_product_cutting}
```
![Метод вычисления максимального произведения при разбиении](../assets/max_product_cutting_greedy_calculation.png)
**Временная сложность зависит от метода реализации операции возведения в степень в языке программирования**. Возьмем Python в качестве примера, существует три часто используемые функции для вычисления степени:
- Оператор `**` и функция `pow()` имеют временную сложность $O(\log a)$.
- Функция `math.pow()` внутренне вызывает функцию `pow()` из библиотеки C, которая выполняет возведение в степень с плавающей точкой, временная сложность составляет $O(1)$.
Переменные $a$ и $b$ используют константное дополнительное пространство, **поэтому пространственная сложность составляет $O(1)$**.
### Доказательство корректности
Используя метод доказательства от противного, рассмотрим только случай $n \geq 4$.
1. **Все множители $\leq 3$**: Предположим, что в оптимальной схеме разбиения существует множитель $x \geq 4$, тогда его определенно можно разбить дальше на $2(x-2)$, получив таким образом большее (или равное) произведение. Это противоречит предположению.
2. **Схема разбиения не содержит $1$**: Предположим, что в оптимальной схеме разбиения существует множитель $1$, тогда его определенно можно объединить с другим множителем для получения большего произведения. Это противоречит предположению.
3. **Схема разбиения содержит не более двух $2$**: Предположим, что в оптимальной схеме разбиения содержится три $2$, тогда их определенно можно заменить на два $3$, произведение будет больше. Это противоречит предположению.
+14
View File
@@ -0,0 +1,14 @@
# Резюме
### Ключевые моменты
- Жадные алгоритмы обычно используются для решения задач оптимизации. Их принцип заключается в том, чтобы на каждом этапе принятия решения делать локально оптимальный выбор в надежде получить глобально оптимальное решение.
- Жадный алгоритм итеративно делает один жадный выбор за другим, на каждом шаге преобразуя задачу в подзадачу меньшего размера, пока задача не будет решена.
- Жадные алгоритмы не только просты в реализации, но и обладают высокой эффективностью решения задач. По сравнению с динамическим программированием временная сложность жадных алгоритмов обычно ниже.
- В задаче о размене монет для некоторых комбинаций монет жадный алгоритм может гарантировать нахождение оптимального решения; для других комбинаций монет это не так, жадный алгоритм может найти очень плохое решение.
- Задачи, подходящие для решения жадным алгоритмом, обладают двумя основными свойствами: свойством жадного выбора и оптимальной подструктурой. Свойство жадного выбора представляет эффективность жадной стратегии.
- Для некоторых сложных задач доказательство свойства жадного выбора не является простым. Относительно проще опровергнуть его, например, в задаче о размене монет.
- Решение жадных задач в основном состоит из трех этапов: анализ задачи, определение жадной стратегии, доказательство корректности. Определение жадной стратегии является ключевым этапом, доказательство корректности часто является сложной задачей.
- Задача о дробном рюкзаке на основе задачи о рюкзаке 0-1 допускает выбор части предмета, поэтому может быть решена с помощью жадного алгоритма. Корректность жадной стратегии можно доказать методом доказательства от противного.
- Задача о максимальной вместимости может быть решена методом полного перебора с временной сложностью $O(n^2)$. Разработав жадную стратегию, при которой на каждом шаге перемещается более короткая сторона внутрь, можно оптимизировать временную сложность до $O(n)$.
- В задаче о максимальном произведении разбиения мы последовательно вывели две жадные стратегии: целые числа $\geq 4$ должны продолжать разбиваться, оптимальный множитель разбиения равен $3$. Код содержит операцию возведения в степень, временная сложность зависит от метода реализации возведения в степень, обычно составляет $O(1)$ или $O(\log n)$.