mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-16 05:30:59 +00:00
First version.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# Задача о дробном рюкзаке
|
||||
|
||||
!!! question
|
||||
|
||||
Дано *n* предметов, где *i*-й предмет имеет массу *wgt*[*i*-1] и стоимость *val*[*i*-1], и рюкзак вместимостью *cap*. Каждый предмет можно выбрать только один раз, **но можно выбрать часть предмета, при этом стоимость рассчитывается пропорционально выбранной массе**. Требуется найти максимальную стоимость предметов в рюкзаке при ограниченной вместимости. Пример показан на рисунке ниже.
|
||||
|
||||

|
||||
|
||||
Задача о дробном рюкзаке и задача о рюкзаке 0-1 в целом очень похожи: состояние включает текущий предмет *i* и вместимость *c*, цель -- найти максимальную стоимость при ограниченной вместимости рюкзака.
|
||||
|
||||
Отличие в том, что в данной задаче допускается выбирать часть предмета. Как показано на рисунке ниже, **можно произвольно разделять предметы и рассчитывать соответствующую стоимость пропорционально массе**.
|
||||
|
||||
1. Для предмета *i* его стоимость на единицу массы равна *val*[*i* - 1]/*wgt*[*i* - 1], сокращенно -- удельная стоимость.
|
||||
2. Предположим, что в рюкзак помещена часть предмета *i* массой *w*, тогда увеличение стоимости рюкзака составит *w* × *val*[*i* - 1]/*wgt*[*i* - 1].
|
||||
|
||||

|
||||
|
||||
### Определение жадной стратегии
|
||||
|
||||
Максимизация общей стоимости предметов в рюкзаке, по сути, является максимизацией стоимости предметов на единицу массы. Из этого можно вывести жадную стратегию, изображенную на рисунке ниже.
|
||||
|
||||
1. Отсортировать предметы по убыванию стоимости на единицу массы.
|
||||
2. Перебирать все предметы и **жадно выбирать на каждом этапе предмет с наивысшей стоимостью на единицу массы**.
|
||||
3. Если оставшейся вместимости рюкзака недостаточно, использовать часть текущего предмета для заполнения рюкзака.
|
||||
|
||||

|
||||
|
||||
### Реализация кода
|
||||
|
||||
Мы создали класс предмета `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***.
|
||||
|
||||
Для других предметов в этом решении мы также можем построить подобное противоречие. В общем, **предметы с большей стоимостью на единицу массы всегда являются более оптимальным выбором**, что доказывает эффективность жадной стратегии.
|
||||
|
||||
Как показано на рисунке ниже, если рассматривать массу предмета и стоимость предмета на единицу массы как горизонтальную и вертикальную оси двумерного графика соответственно, то задача о дробном рюкзаке может быть преобразована в «нахождение максимальной площади в ограниченном интервале горизонтальной оси». Эта аналогия может помочь понять эффективность жадной стратегии с геометрической точки зрения.
|
||||
|
||||

|
||||
@@ -0,0 +1,91 @@
|
||||
# Жадные алгоритмы
|
||||
|
||||
<u>Жадный алгоритм</u> -- это распространенный метод решения задач оптимизации. Его основная идея заключается в том, чтобы на каждом этапе принятия решения выбирать наиболее оптимальный на данный момент вариант, т. е. с жадностью принимать локально оптимальные решения в надежде получить глобально оптимальное решение. Жадные алгоритмы просты и эффективны, и они находят широкое применение в решении многих практических задач.
|
||||
|
||||
Жадные алгоритмы и динамическое программирование часто используются для решения задач оптимизации. Между ними есть некоторые сходства, например оба метода зависят от свойств оптимальной подструктуры, но их принципы работы различны.
|
||||
|
||||
- Динамическое программирование для получения текущего решения учитывает все предыдущие решения и использует решения предыдущих подзадач для построения решения текущей подзадачи.
|
||||
- Жадный алгоритм не учитывает предыдущие решения, а просто движется вперед, делая жадные выборы и постепенно сокращая область задачи, пока она не будет решена.
|
||||
|
||||
Чтобы лучше понять принцип работы жадного алгоритма, рассмотрим его применение к задаче о размене монет. Она уже была рассмотрена в разделе «Задача о полном рюкзаке», и, вероятно, вы с ней уже знакомы.
|
||||
|
||||
!!! question
|
||||
|
||||
Дано *n* видов монет, номинал *i*-й монеты равен *coins*[*i* - 1], целевая сумма *amt*, каждый вид монет можно выбирать многократно. Найти минимальное количество монет, необходимое для получения целевой суммы. Если получить целевую сумму невозможно, вернуть -1.
|
||||
|
||||
Жадная стратегия, применяемая в этой задаче, показана на следующем рисунке. Для заданной целевой суммы **мы жадно выбираем монету, которая не превышает и наиболее близка к этой сумме**, и повторяем этот шаг, пока не будет достигнута целевая сумма.
|
||||
|
||||

|
||||
|
||||
Ниже приведен код реализации.
|
||||
|
||||
```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 монеты.
|
||||
|
||||

|
||||
|
||||
Таким образом, для задачи размена монет жадный алгоритм не гарантирует нахождение глобально оптимального решения и может привести к очень плохому решению. Для решения этой задачи лучше подходит динамическое программирование.
|
||||
|
||||
В общем случае жадные алгоритмы применимы в следующих двух ситуациях:
|
||||
|
||||
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. **доказательство корректности**: обычно требуется доказать наличие свойства жадного выбора и оптимальной подструктуры задачи. Этот этап может потребовать использования математических доказательств, таких как метод математической индукции или доказательство от противного.
|
||||
|
||||
Определение жадной стратегии является ключевым этапом решения задачи, но его реализация может быть непростой по следующим причинам.
|
||||
|
||||
- **Жадные стратегии для различных задач могут значительно различаться**. Для многих задач жадная стратегия очевидна, и ее можно определить с помощью общего размышления и эмпирических проб. Однако для некоторых сложных задач жадная стратегия может оказаться очень скрытой, что потребует значительного опыта в решении задач и навыков работы с алгоритмами.
|
||||
- **Некоторые жадные стратегии могут быть обманчивыми**. Бывает, жадная стратегия разработана с полной уверенностью в ее правильности, код написан и отправлен на выполнение. Но оказывается, что некоторые тестовые примеры не проходят проверку на корректность. Это происходит потому, что разработанная жадная стратегия является лишь частично правильной, как в случае с задачей о размене монет, описанной выше.
|
||||
|
||||
Для обеспечения корректности необходимо провести строгое математическое доказательство жадной стратегии, **обычно с использованием метода доказательства от противного или метода математической индукции**.
|
||||
|
||||
Тем не менее доказательство корректности может оказаться непростой задачей. Если нет ясности, обычно выбирается отладка кода на основе тестовых примеров с постепенной модификацией и проверкой жадной стратегии.
|
||||
|
||||
## Типичные задачи для жадного алгоритма
|
||||
|
||||
Жадный алгоритм часто применяется в задачах оптимизации, удовлетворяющих свойству жадного выбора и оптимальной подструктуре. Ниже перечислены некоторые типичные задачи для жадного алгоритма.
|
||||
|
||||
- **Задача о размене монет**: при некоторых комбинациях монет жадный алгоритм всегда может получить оптимальное решение.
|
||||
- **Задача о расписании интервалов**: пусть у вас есть несколько задач, каждая из которых выполняется в течение определенного времени, и ваша цель -- выполнить как можно больше задач. Если каждый раз выбирать задачу с наименьшим временем окончания, то жадный алгоритм может дать оптимальное решение.
|
||||
- **Задача о дробном рюкзаке**: дана группа предметов и вместимость. Ваша цель -- выбрать группу предметов так, чтобы
|
||||
@@ -0,0 +1,9 @@
|
||||
# Жадность
|
||||
|
||||

|
||||
|
||||
!!! abstract
|
||||
|
||||
Подсолнух поворачивается к солнцу, постоянно стремясь к максимальному росту.
|
||||
|
||||
Жадная стратегия через серию простых выборов постепенно приводит к наилучшему ответу.
|
||||
@@ -0,0 +1,99 @@
|
||||
# Задача о максимальной вместимости
|
||||
|
||||
!!! 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$ внутрь**. Ранее мы уже доказали, что перемещение длинной перегородки внутрь обязательно приводит к уменьшению вместимости. То есть пропущенные состояния не могут быть оптимальным решением, **их пропуск не приведет к потере оптимального решения**.
|
||||
|
||||
Приведенный выше анализ показывает, что операция перемещения короткой перегородки является «безопасной», жадная стратегия эффективна.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Задача о максимальном произведении при разбиении
|
||||
|
||||
!!! question
|
||||
|
||||
Дано положительное целое число $n$, которое нужно разбить на сумму как минимум двух положительных целых чисел. Найдите максимальное произведение всех целых чисел после разбиения, как показано на рисунке ниже.
|
||||
|
||||

|
||||
|
||||
Предположим, что мы разбиваем $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$.
|
||||
|
||||

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

|
||||
|
||||
Исходя из вышесказанного, можно вывести следующую жадную стратегию:
|
||||
|
||||
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}
|
||||
```
|
||||
|
||||

|
||||
|
||||
**Временная сложность зависит от метода реализации операции возведения в степень в языке программирования**. Возьмем 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$, произведение будет больше. Это противоречит предположению.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Резюме
|
||||
|
||||
### Ключевые моменты
|
||||
|
||||
- Жадные алгоритмы обычно используются для решения задач оптимизации. Их принцип заключается в том, чтобы на каждом этапе принятия решения делать локально оптимальный выбор в надежде получить глобально оптимальное решение.
|
||||
- Жадный алгоритм итеративно делает один жадный выбор за другим, на каждом шаге преобразуя задачу в подзадачу меньшего размера, пока задача не будет решена.
|
||||
- Жадные алгоритмы не только просты в реализации, но и обладают высокой эффективностью решения задач. По сравнению с динамическим программированием временная сложность жадных алгоритмов обычно ниже.
|
||||
- В задаче о размене монет для некоторых комбинаций монет жадный алгоритм может гарантировать нахождение оптимального решения; для других комбинаций монет это не так, жадный алгоритм может найти очень плохое решение.
|
||||
- Задачи, подходящие для решения жадным алгоритмом, обладают двумя основными свойствами: свойством жадного выбора и оптимальной подструктурой. Свойство жадного выбора представляет эффективность жадной стратегии.
|
||||
- Для некоторых сложных задач доказательство свойства жадного выбора не является простым. Относительно проще опровергнуть его, например, в задаче о размене монет.
|
||||
- Решение жадных задач в основном состоит из трех этапов: анализ задачи, определение жадной стратегии, доказательство корректности. Определение жадной стратегии является ключевым этапом, доказательство корректности часто является сложной задачей.
|
||||
- Задача о дробном рюкзаке на основе задачи о рюкзаке 0-1 допускает выбор части предмета, поэтому может быть решена с помощью жадного алгоритма. Корректность жадной стратегии можно доказать методом доказательства от противного.
|
||||
- Задача о максимальной вместимости может быть решена методом полного перебора с временной сложностью $O(n^2)$. Разработав жадную стратегию, при которой на каждом шаге перемещается более короткая сторона внутрь, можно оптимизировать временную сложность до $O(n)$.
|
||||
- В задаче о максимальном произведении разбиения мы последовательно вывели две жадные стратегии: целые числа $\geq 4$ должны продолжать разбиваться, оптимальный множитель разбиения равен $3$. Код содержит операцию возведения в степень, временная сложность зависит от метода реализации возведения в степень, обычно составляет $O(1)$ или $O(\log n)$.
|
||||
Reference in New Issue
Block a user