mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-24 12:06:07 +00:00
fix Ru translation (#1894)
* docs(ru): replace prose quotes with guillemets * docs(ru): replace prose semicolons with periods * docs(ru): align animation title forms * docs(ru): align figure and table references
This commit is contained in:
@@ -9,27 +9,27 @@
|
||||
|
||||
В целом, если задача содержит перекрывающиеся подзадачи, оптимальную подструктуру и удовлетворяет свойству отсутствия последствий, то она обычно подходит для решения с помощью динамического программирования. Однако извлечь все эти свойства напрямую из формулировки задачи бывает трудно. Поэтому на практике мы обычно ослабляем требования и **сначала смотрим, подходит ли задача для решения методом поиска с возвратом (полного перебора)**.
|
||||
|
||||
**Задачи, подходящие для поиска с возвратом, обычно удовлетворяют "модели дерева решений"**. Такие задачи можно описать деревом, где каждый узел представляет одно решение, а каждый путь представляет последовательность решений.
|
||||
**Задачи, подходящие для поиска с возвратом, обычно удовлетворяют «модели дерева решений»**. Такие задачи можно описать деревом, где каждый узел представляет одно решение, а каждый путь представляет последовательность решений.
|
||||
|
||||
Иначе говоря, если в задаче есть четко выраженные решения и ответ порождается последовательностью таких решений, то она удовлетворяет модели дерева решений и обычно допускает решение через поиск с возвратом.
|
||||
|
||||
Поверх этого у задач динамического программирования есть и некоторые дополнительные "плюсы".
|
||||
Поверх этого у задач динамического программирования есть и некоторые дополнительные «плюсы».
|
||||
|
||||
- В условии задачи фигурируют слова "максимальный", "минимальный", "наибольший", "наименьший" и другие формулировки оптимизации.
|
||||
- В условии задачи фигурируют слова «максимальный», «минимальный», «наибольший», «наименьший» и другие формулировки оптимизации.
|
||||
- Состояния задачи можно описать списком, многомерной матрицей или деревом, и между состоянием и соседними состояниями существует рекуррентная зависимость.
|
||||
|
||||
Соответственно, существуют и некоторые "минусы".
|
||||
Соответственно, существуют и некоторые «минусы».
|
||||
|
||||
- Цель задачи состоит в поиске всех возможных решений, а не одного оптимального решения.
|
||||
- В формулировке явно присутствуют признаки комбинаторного перечисления, и требуется вернуть сразу много конкретных вариантов.
|
||||
|
||||
Если задача удовлетворяет модели дерева решений и имеет достаточно явные "плюсы", мы можем предположить, что это задача динамического программирования, а затем проверить это предположение уже в процессе решения.
|
||||
Если задача удовлетворяет модели дерева решений и имеет достаточно явные «плюсы», мы можем предположить, что это задача динамического программирования, а затем проверить это предположение уже в процессе решения.
|
||||
|
||||
## Этапы решения задачи
|
||||
|
||||
Конкретный процесс решения задач динамического программирования зависит от природы и сложности задачи, но обычно включает следующие шаги: описание решений, определение состояний, построение таблицы $dp$ , вывод уравнения перехода состояния, определение граничных условий и порядка переходов.
|
||||
|
||||
Чтобы нагляднее показать этот процесс, рассмотрим классическую задачу "минимальная сумма пути".
|
||||
Чтобы нагляднее показать этот процесс, рассмотрим классическую задачу «минимальная сумма пути».
|
||||
|
||||
!!! question
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
**Шаг 1: понять решения на каждом раунде, определить состояние и тем самым получить таблицу $dp$**
|
||||
|
||||
В этой задаче на каждом раунде решение состоит в том, чтобы из текущей клетки сделать один шаг вниз или вправо. Пусть индексы строки и столбца текущей клетки равны $[i, j]$ ; тогда после шага вниз или вправо индексы становятся равными $[i+1, j]$ или $[i, j+1]$ . Значит, состояние должно включать два переменных индекса: строки и столбца, то есть состояние обозначается как $[i, j]$ .
|
||||
В этой задаче на каждом раунде решение состоит в том, чтобы из текущей клетки сделать один шаг вниз или вправо. Пусть индексы строки и столбца текущей клетки равны $[i, j]$. Тогда после шага вниз или вправо индексы становятся равными $[i+1, j]$ или $[i, j+1]$ . Значит, состояние должно включать два переменных индекса: строки и столбца, то есть состояние обозначается как $[i, j]$ .
|
||||
|
||||
Подзадача, соответствующая состоянию $[i, j]$ , такова: минимальная сумма пути от стартовой клетки $[0, 0]$ до клетки $[i, j]$ . Ее решение обозначается через $dp[i, j]$ .
|
||||
|
||||
@@ -52,8 +52,8 @@
|
||||
!!! note
|
||||
|
||||
Как в динамическом программировании, так и в поиске с возвратом, решение задачи можно описать как последовательность решений, а состояние образуется всеми переменными решений. Оно должно содержать всю информацию, достаточную для вывода следующего состояния.
|
||||
|
||||
Каждому состоянию соответствует некоторая подзадача, и для хранения решений всех подзадач мы определяем таблицу $dp$ ; каждая независимая переменная состояния становится одним измерением таблицы $dp$ . По сути таблица $dp$ - это отображение от состояния к решению соответствующей подзадачи.
|
||||
|
||||
Каждому состоянию соответствует некоторая подзадача, и для хранения решений всех подзадач мы определяем таблицу $dp$. Каждая независимая переменная состояния становится одним измерением таблицы $dp$ . По сути таблица $dp$ - это отображение от состояния к решению соответствующей подзадачи.
|
||||
|
||||
**Шаг 2: найти оптимальную подструктуру и на ее основе вывести уравнение перехода состояния**
|
||||
|
||||
@@ -84,10 +84,10 @@ $$
|
||||
!!! note
|
||||
|
||||
В динамическом программировании граничные условия используются для инициализации таблицы $dp$ , а в поиске - для обрезки.
|
||||
|
||||
|
||||
Смысл порядка перехода состояния в том, чтобы к моменту вычисления текущей подзадачи все более мелкие подзадачи, от которых она зависит, уже были вычислены корректно.
|
||||
|
||||
После этого анализа мы уже можем напрямую написать код динамического программирования. Однако разложение на подзадачи - это мышление "сверху вниз", поэтому с точки зрения мышления более естественно реализовывать задачу в порядке "полный перебор $\rightarrow$ поиск с мемоизацией $\rightarrow$ динамическое программирование".
|
||||
После этого анализа мы уже можем напрямую написать код динамического программирования. Однако разложение на подзадачи - это мышление «сверху вниз», поэтому с точки зрения мышления более естественно реализовывать задачу в порядке «полный перебор $\rightarrow$ поиск с мемоизацией $\rightarrow$ динамическое программирование».
|
||||
|
||||
### Метод 1: полный перебор
|
||||
|
||||
@@ -104,7 +104,7 @@ $$
|
||||
[file]{min_path_sum}-[class]{}-[func]{min_path_sum_dfs}
|
||||
```
|
||||
|
||||
На рисунке ниже показано дерево рекурсии с корнем в $dp[2, 1]$ ; в нем содержатся перекрывающиеся подзадачи, и их число будет резко расти вместе с размером сетки `grid` .
|
||||
На рисунке ниже показано дерево рекурсии с корнем в $dp[2, 1]$. В нем содержатся перекрывающиеся подзадачи, и их число будет резко расти вместе с размером сетки `grid` .
|
||||
|
||||
По своей сути причина появления перекрывающихся подзадач такова: **существует много разных путей от левого верхнего угла до одной и той же клетки**.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user