This commit is contained in:
krahets
2026-03-30 08:17:41 +08:00
parent 68cafe99dd
commit 46bccf0065
484 changed files with 60193 additions and 20315 deletions
@@ -65,8 +65,8 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Noto+Sans:300,300i,400,400i,500,500i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Noto Sans";--md-code-font:"JetBrains Mono"}</style>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=PT+Sans:300,300i,400,400i,500,500i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"PT Sans";--md-code-font:"JetBrains Mono"}</style>
@@ -574,7 +574,7 @@
<span class="md-ellipsis">
Глава 1. Знакомство с алгоритмами
Глава 1. Введение в алгоритмы
@@ -596,7 +596,7 @@
<span class="md-nav__icon md-icon"></span>
Глава 1. Знакомство с алгоритмами
Глава 1. Введение в алгоритмы
</label>
@@ -646,7 +646,7 @@
<span class="md-ellipsis">
1.2 Что такое структуры данных и алгоритмы
1.2 Что такое алгоритм
@@ -1181,7 +1181,7 @@
<span class="md-ellipsis">
Глава 4. Массив и связный список
Глава 4. Массивы и списки
@@ -1203,7 +1203,7 @@
<span class="md-nav__icon md-icon"></span>
Глава 4. Массив и связный список
Глава 4. Массивы и списки
</label>
@@ -1309,7 +1309,7 @@
<span class="md-ellipsis">
4.4 Память и кеш *
4.4 Оперативная память и кэш *
@@ -1591,7 +1591,7 @@
<span class="md-ellipsis">
Глава 6. Хеширование
Глава 6. Хеш-таблицы
@@ -1613,7 +1613,7 @@
<span class="md-nav__icon md-icon"></span>
Глава 6. Хеширование
Глава 6. Хеш-таблицы
</label>
@@ -1691,7 +1691,7 @@
<span class="md-ellipsis">
6.3 Хеш-алгоритмы
6.3 Алгоритмы хеширования
@@ -1786,7 +1786,7 @@
<span class="md-ellipsis">
Глава 7. Дерево
Глава 7. Деревья
@@ -1808,7 +1808,7 @@
<span class="md-nav__icon md-icon"></span>
Глава 7. Дерево
Глава 7. Деревья
</label>
@@ -1886,7 +1886,7 @@
<span class="md-ellipsis">
7.3 Представление дерева массивом
7.3 Представление двоичного дерева массивом
@@ -1970,7 +1970,7 @@
<span class="md-ellipsis">
7.6 Резюме
7.6 Краткие итоги
@@ -2133,7 +2133,7 @@
<span class="md-ellipsis">
8.3 Задача Top-K
8.3 Задача Top-k
@@ -2224,7 +2224,7 @@
<span class="md-ellipsis">
Глава 9. Граф
Глава 9. Графы
@@ -2246,7 +2246,7 @@
<span class="md-nav__icon md-icon"></span>
Глава 9. Граф
Глава 9. Графы
</label>
@@ -2296,7 +2296,7 @@
<span class="md-ellipsis">
9.2 Базовые операции над графами
9.2 Базовые операции графа
@@ -2352,7 +2352,7 @@
<span class="md-ellipsis">
9.4 Резюме
9.4 Краткие итоги
@@ -2491,7 +2491,7 @@
<span class="md-ellipsis">
10.2 Точка вставки двоичного поиска
10.2 Двоичный поиск точки вставки
@@ -2519,7 +2519,7 @@
<span class="md-ellipsis">
10.3 Граничные случаи двоичного поиска
10.3 Двоичный поиск границ
@@ -2547,7 +2547,7 @@
<span class="md-ellipsis">
10.4 Стратегия оптимизации через хеширование
10.4 Стратегии оптимизации хеширования
@@ -2575,7 +2575,7 @@
<span class="md-ellipsis">
10.5 Алгоритмы поиска: новый взгляд
10.5 Переосмысление алгоритмов поиска
@@ -2780,7 +2780,7 @@
<span class="md-ellipsis">
11.3 Пузырьковая сортировка
11.3 Сортировка пузырьком
@@ -2808,7 +2808,7 @@
<span class="md-ellipsis">
11.4 Сортировка вставкой
11.4 Сортировка вставками
@@ -3113,7 +3113,7 @@
<span class="md-ellipsis">
12.1 Алгоритмы разделяй и властвуй
12.1 Стратегия разделяй и властвуй
@@ -3141,7 +3141,7 @@
<span class="md-ellipsis">
12.2 Стратегия поиска разделяй и властвуй
12.2 Поисковая стратегия разделяй и властвуй
@@ -3418,7 +3418,7 @@
<span class="md-ellipsis">
13.4 Задача о $n$ ферзях
13.4 Задача о n ферзях
@@ -3561,7 +3561,7 @@
<span class="md-ellipsis">
14.1 Введение в динамическое программирование
14.1 Первое знакомство с динамическим программированием
@@ -3682,7 +3682,7 @@
<span class="md-ellipsis">
14.5 Задача о неограниченном рюкзаке
14.5 Задача о полном рюкзаке
@@ -3700,7 +3700,7 @@
<span class="md-ellipsis">
14.5 Задача о неограниченном рюкзаке
14.5 Задача о полном рюкзаке
@@ -4611,7 +4611,7 @@
<!-- Page content -->
<h1 id="145">14.5 &nbsp; Задача о полном рюкзаке<a class="headerlink" href="#145" title="Permanent link">&para;</a></h1>
<p>В этом разделе сначала решим еще один распространенный вариант задачи о рюкзаке - полный рюкзак, а затем рассмотрим одну из его типичных специальных форм: задачу о размене монет.</p>
<p>В этом разделе сначала решим еще одну распространенную задачу о рюкзаке - задачу о полном рюкзаке, а затем рассмотрим один из ее типичных частных случаев: задачу о размене монет.</p>
<h2 id="1451">14.5.1 &nbsp; Задача о полном рюкзаке<a class="headerlink" href="#1451" title="Permanent link">&para;</a></h2>
<div class="admonition question">
<p class="admonition-title">Question</p>
@@ -4621,10 +4621,10 @@
<p align="center"> Рисунок 14-22 &nbsp; Пример данных для задачи о полном рюкзаке </p>
<h3 id="1">1. &nbsp; Идея динамического программирования<a class="headerlink" href="#1" title="Permanent link">&para;</a></h3>
<p>Задача о полном рюкзаке очень похожа на задачу о рюкзаке 0-1; <strong>разница состоит только в том, что число выборов каждого предмета не ограничено</strong>.</p>
<p>Задача о полном рюкзаке очень похожа на задачу о рюкзаке 0-1; <strong>разница состоит только в том, что количество выборов каждого предмета не ограничено</strong>.</p>
<ul>
<li>В задаче о рюкзаке 0-1 каждого предмета существует только один экземпляр, поэтому после того как предмет <span class="arithmatex">\(i\)</span> помещен в рюкзак, выбирать можно только из первых <span class="arithmatex">\(i-1\)</span> предметов.</li>
<li>В задаче о полном рюкзаке число экземпляров каждого предмета бесконечно, поэтому после того как предмет <span class="arithmatex">\(i\)</span> помещен в рюкзак, <strong>выбирать все еще можно из первых <span class="arithmatex">\(i\)</span> предметов</strong>.</li>
<li>В задаче о полном рюкзаке количество предметов не ограничено, поэтому после того как предмет <span class="arithmatex">\(i\)</span> помещен в рюкзак, <strong>можно продолжать выбирать из первых <span class="arithmatex">\(i\)</span> предметов</strong>.</li>
</ul>
<p>При этом состояние <span class="arithmatex">\([i, c]\)</span> в задаче о полном рюкзаке может изменяться двумя способами.</p>
<ul>
@@ -5315,9 +5315,9 @@ dp[i, c] = \max(dp[i-1, c], dp[i, c - wgt[i-1]] + val[i-1])
<p align="center"> Рисунок 14-24 &nbsp; Пример данных для задачи о размене монет </p>
<h3 id="1_1">1. &nbsp; Идея динамического программирования<a class="headerlink" href="#1_1" title="Permanent link">&para;</a></h3>
<p><strong>Задачу о размене монет можно рассматривать как частный случай задачи о полном рюкзаке</strong> ; между ними существует следующая связь и следующие различия.</p>
<p><strong>Задачу о размене монет можно рассматривать как частный случай задачи о полном рюкзаке</strong> ; между ними существуют следующие соответствия и различия.</p>
<ul>
<li>Эти две задачи можно взаимно переводить друг в друга: "предмет" соответствует "монете", "вес предмета" соответствует "номиналу монеты", а "вместимость рюкзака" соответствует "целевой сумме".</li>
<li>Эти две задачи можно взаимно преобразовать: "предмет" соответствует "монете", "вес предмета" соответствует "номиналу монеты", а "вместимость рюкзака" соответствует "целевой сумме".</li>
<li>Цель оптимизации противоположна: в задаче о полном рюкзаке нужно максимизировать стоимость предметов, а в задаче о размене монет - минимизировать число монет.</li>
<li>В задаче о полном рюкзаке ищется решение, не превышающее вместимость, а в задаче о размене монет требуется <strong>ровно</strong> набрать целевую сумму.</li>
</ul>
@@ -5337,7 +5337,7 @@ dp[i, a] = \min(dp[i-1, a], dp[i, a - coins[i-1]] + 1)
<p>Когда целевая сумма равна <span class="arithmatex">\(0\)</span> , минимальное число монет для ее набора равно <span class="arithmatex">\(0\)</span> , то есть весь первый столбец <span class="arithmatex">\(dp[i, 0]\)</span> заполняется нулями.</p>
<p>Когда монет нет, <strong>невозможно набрать никакую целевую сумму <span class="arithmatex">\(&gt; 0\)</span></strong> ; это и есть недопустимое решение. Чтобы функция <span class="arithmatex">\(\min()\)</span> в уравнении перехода состояния могла распознавать и отбрасывать такие недопустимые решения, удобно использовать значение <span class="arithmatex">\(+ \infty\)</span> ; то есть всю первую строку <span class="arithmatex">\(dp[0, a]\)</span> нужно инициализировать значением <span class="arithmatex">\(+ \infty\)</span> .</p>
<h3 id="2_1">2. &nbsp; Реализация кода<a class="headerlink" href="#2_1" title="Permanent link">&para;</a></h3>
<p>Большинство языков программирования не предоставляет готовую переменную <span class="arithmatex">\(+ \infty\)</span> для целых чисел, поэтому обычно приходится заменять ее на максимальное значение типа <code>int</code> . Но тогда возникает риск переполнения: операция <span class="arithmatex">\(+ 1\)</span> в уравнении перехода может переполнить большое число.</p>
<p>Большинство языков программирования не предоставляет представление для <span class="arithmatex">\(+ \infty\)</span> в целочисленном виде, поэтому обычно приходится заменять его на максимальное значение типа <code>int</code> . Но тогда возникает риск переполнения: операция <span class="arithmatex">\(+ 1\)</span> в уравнении перехода может переполнить большое число.</p>
<p>Поэтому здесь мы используем число <span class="arithmatex">\(amt + 1\)</span> как обозначение недопустимого решения, потому что для набора суммы <span class="arithmatex">\(amt\)</span> максимум нужно не больше чем <span class="arithmatex">\(amt\)</span> монет. Перед возвратом результата проверяем, равно ли <span class="arithmatex">\(dp[n, amt]\)</span> значению <span class="arithmatex">\(amt + 1\)</span> ; если да, то возвращаем <span class="arithmatex">\(-1\)</span> , что означает невозможность набрать целевую сумму. Код приведен ниже:</p>
<div class="tabbed-set tabbed-alternate" data-tabs="4:13"><input checked="checked" id="__tabbed_4_1" name="__tabbed_4" type="radio" /><input id="__tabbed_4_2" name="__tabbed_4" type="radio" /><input id="__tabbed_4_3" name="__tabbed_4" type="radio" /><input id="__tabbed_4_4" name="__tabbed_4" type="radio" /><input id="__tabbed_4_5" name="__tabbed_4" type="radio" /><input id="__tabbed_4_6" name="__tabbed_4" type="radio" /><input id="__tabbed_4_7" name="__tabbed_4" type="radio" /><input id="__tabbed_4_8" name="__tabbed_4" type="radio" /><input id="__tabbed_4_9" name="__tabbed_4" type="radio" /><input id="__tabbed_4_10" name="__tabbed_4" type="radio" /><input id="__tabbed_4_11" name="__tabbed_4" type="radio" /><input id="__tabbed_4_12" name="__tabbed_4" type="radio" /><input id="__tabbed_4_13" name="__tabbed_4" type="radio" /><div class="tabbed-labels"><label for="__tabbed_4_1">Python</label><label for="__tabbed_4_2">C++</label><label for="__tabbed_4_3">Java</label><label for="__tabbed_4_4">C#</label><label for="__tabbed_4_5">Go</label><label for="__tabbed_4_6">Swift</label><label for="__tabbed_4_7">JS</label><label for="__tabbed_4_8">TS</label><label for="__tabbed_4_9">Dart</label><label for="__tabbed_4_10">Rust</label><label for="__tabbed_4_11">C</label><label for="__tabbed_4_12">Kotlin</label><label for="__tabbed_4_13">Ruby</label></div>
<div class="tabbed-content">
@@ -6118,7 +6118,7 @@ dp[i, a] = \min(dp[i-1, a], dp[i, a - coins[i-1]] + 1)
<p align="center"> Рисунок 14-26 &nbsp; Пример данных для задачи о размене монет II </p>
<h3 id="1_2">1. &nbsp; Идея динамического программирования<a class="headerlink" href="#1_2" title="Permanent link">&para;</a></h3>
<p>По сравнению с предыдущей задачей теперь целью является число комбинаций. Поэтому подзадача меняется на следующую: <strong>число комбинаций из первых <span class="arithmatex">\(i\)</span> видов монет, которыми можно набрать сумму <span class="arithmatex">\(a\)</span></strong>. При этом таблица <span class="arithmatex">\(dp\)</span> по-прежнему остается двумерной матрицей размера <span class="arithmatex">\((n+1) \times (amt + 1)\)</span> .</p>
<p>По сравнению с предыдущей задачей здесь целью является число комбинаций. Поэтому подзадача меняется на следующую: <strong>число комбинаций из первых <span class="arithmatex">\(i\)</span> видов монет, которыми можно набрать сумму <span class="arithmatex">\(a\)</span></strong>. При этом таблица <span class="arithmatex">\(dp\)</span> по-прежнему остается двумерной матрицей размера <span class="arithmatex">\((n+1) \times (amt + 1)\)</span> .</p>
<p>Число комбинаций для текущего состояния равно сумме числа комбинаций для двух решений: не брать текущую монету и брать текущую монету. Поэтому уравнение перехода состояния принимает вид:</p>
<div class="arithmatex">\[
dp[i, a] = dp[i-1, a] + dp[i, a - coins[i-1]]