This commit is contained in:
krahets
2026-04-14 18:06:19 +08:00
parent 17b2a0b630
commit cf0747ba3e
131 changed files with 604 additions and 609 deletions
@@ -4494,7 +4494,7 @@
<h2 id="1331">13.3.1 &nbsp; Случай без повторяющихся элементов<a class="headerlink" href="#1331" title="Permanent link">&para;</a></h2>
<div class="admonition question">
<p class="admonition-title">Question</p>
<p>Дан массив положительных целых чисел <code>nums</code> и целое положительное значение <code>target</code> . Найдите все возможные комбинации, сумма элементов которых равна <code>target</code> . Во входном массиве нет повторяющихся элементов, и каждый элемент можно выбирать неограниченное число раз. Верните эти комбинации в виде списка; в результате не должно быть повторяющихся комбинаций.</p>
<p>Дан массив положительных целых чисел <code>nums</code> и целое положительное значение <code>target</code> . Найдите все возможные комбинации, сумма элементов которых равна <code>target</code> . Во входном массиве нет повторяющихся элементов, и каждый элемент можно выбирать неограниченное число раз. Верните эти комбинации в виде списка. В результате не должно быть повторяющихся комбинаций.</p>
</div>
<p>Например, для входного множества <span class="arithmatex">\(\{3, 4, 5\}\)</span> и целевого значения <span class="arithmatex">\(9\)</span> решениями будут <span class="arithmatex">\(\{3, 3, 3\}\)</span> и <span class="arithmatex">\(\{4, 5\}\)</span> . При этом важно учитывать два обстоятельства.</p>
<ul>
@@ -4502,7 +4502,7 @@
<li>Подмножество не различает порядок элементов, поэтому <span class="arithmatex">\(\{4, 5\}\)</span> и <span class="arithmatex">\(\{5, 4\}\)</span> считаются одним и тем же подмножеством.</li>
</ul>
<h3 id="1">1. &nbsp; Отталкиваемся от решения задачи о перестановках<a class="headerlink" href="#1" title="Permanent link">&para;</a></h3>
<p>Как и в задаче о перестановках, можно представлять построение подмножеств как результат последовательности выборов и во время выбора динамически обновлять "сумму элементов"; когда эта сумма становится равной <code>target</code> , соответствующее подмножество записывается в список результатов.</p>
<p>Как и в задаче о перестановках, можно представлять построение подмножеств как результат последовательности выборов и во время выбора динамически обновлять «сумму элементов». Когда эта сумма становится равной <code>target</code> , соответствующее подмножество записывается в список результатов.</p>
<p>Однако в отличие от задачи о перестановках <strong>в этой задаче элементы множества можно выбирать неограниченное число раз</strong>, поэтому нам не нужен булев список <code>selected</code> для записи того, был ли выбран элемент. Можно слегка изменить код для перестановок и получить первоначальную версию решения:</p>
<div class="tabbed-set tabbed-alternate" data-tabs="1:13"><input checked="checked" id="__tabbed_1_1" name="__tabbed_1" type="radio" /><input id="__tabbed_1_2" name="__tabbed_1" type="radio" /><input id="__tabbed_1_3" name="__tabbed_1" type="radio" /><input id="__tabbed_1_4" name="__tabbed_1" type="radio" /><input id="__tabbed_1_5" name="__tabbed_1" type="radio" /><input id="__tabbed_1_6" name="__tabbed_1" type="radio" /><input id="__tabbed_1_7" name="__tabbed_1" type="radio" /><input id="__tabbed_1_8" name="__tabbed_1" type="radio" /><input id="__tabbed_1_9" name="__tabbed_1" type="radio" /><input id="__tabbed_1_10" name="__tabbed_1" type="radio" /><input id="__tabbed_1_11" name="__tabbed_1" type="radio" /><input id="__tabbed_1_12" name="__tabbed_1" type="radio" /><input id="__tabbed_1_13" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">Python</label><label for="__tabbed_1_2">C++</label><label for="__tabbed_1_3">Java</label><label for="__tabbed_1_4">C#</label><label for="__tabbed_1_5">Go</label><label for="__tabbed_1_6">Swift</label><label for="__tabbed_1_7">JS</label><label for="__tabbed_1_8">TS</label><label for="__tabbed_1_9">Dart</label><label for="__tabbed_1_10">Rust</label><label for="__tabbed_1_11">C</label><label for="__tabbed_1_12">Kotlin</label><label for="__tabbed_1_13">Ruby</label></div>
<div class="tabbed-content">
@@ -4999,7 +4999,7 @@
<li>Сравнение подмножеств (то есть массивов) само по себе довольно затратно: сначала приходится сортировать массивы, а затем поэлементно сравнивать их.</li>
</ul>
<h3 id="2">2. &nbsp; Обрезка повторяющихся подмножеств<a class="headerlink" href="#2" title="Permanent link">&para;</a></h3>
<p><strong>Поэтому стоит выполнять устранение дубликатов прямо во время поиска, с помощью обрезки</strong>. Посмотрите на рисунок 13-11: повторяющиеся подмножества возникают тогда, когда элементы массива выбираются в разном порядке, например так.</p>
<p><strong>Поэтому стоит выполнять устранение дубликатов прямо во время поиска, с помощью обрезки</strong>. Как видно на рисунке 13-11, повторяющиеся подмножества возникают тогда, когда элементы массива выбираются в разном порядке, например так.</p>
<ol>
<li>Если в первом и втором раундах выбрать соответственно <span class="arithmatex">\(3\)</span> и <span class="arithmatex">\(4\)</span> , то будут сгенерированы все подмножества, содержащие эти два элемента, и их можно обозначить как <span class="arithmatex">\([3, 4, \dots]\)</span> .</li>
<li>После этого, если в первом раунде выбрать <span class="arithmatex">\(4\)</span> , <strong>то во втором раунде нужно пропустить <span class="arithmatex">\(3\)</span></strong> , потому что подмножества <span class="arithmatex">\([4, 3, \dots]\)</span> полностью дублируют подмножества, уже построенные на шаге <code>1.</code> .</li>
@@ -5013,13 +5013,13 @@
<p><img alt="Повторяющиеся подмножества из-за разного порядка выбора" class="animation-figure" src="../subset_sum_problem.assets/subset_sum_i_pruning.png" /></p>
<p align="center"> Рисунок 13-11 &nbsp; Повторяющиеся подмножества из-за разного порядка выбора </p>
<p>В общем виде, если входной массив имеет вид <span class="arithmatex">\([x_1, x_2, \dots, x_n]\)</span> , а последовательность выборов в ходе поиска равна <span class="arithmatex">\([x_{i_1}, x_{i_2}, \dots, x_{i_m}]\)</span> , то она должна удовлетворять условию <span class="arithmatex">\(i_1 \leq i_2 \leq \dots \leq i_m\)</span> ; <strong>все последовательности выборов, не удовлетворяющие этому условию, приводят к дубликатам и должны отсекаться</strong>.</p>
<p>В общем виде, если входной массив имеет вид <span class="arithmatex">\([x_1, x_2, \dots, x_n]\)</span> , а последовательность выборов в ходе поиска равна <span class="arithmatex">\([x_{i_1}, x_{i_2}, \dots, x_{i_m}]\)</span> , то она должна удовлетворять условию <span class="arithmatex">\(i_1 \leq i_2 \leq \dots \leq i_m\)</span>. <strong>Все последовательности выборов, не удовлетворяющие этому условию, приводят к дубликатам и должны отсекаться</strong>.</p>
<h3 id="3">3. &nbsp; Реализация кода<a class="headerlink" href="#3" title="Permanent link">&para;</a></h3>
<p>Чтобы реализовать такую обрезку, инициализируем переменную <code>start</code> , которая будет указывать начальную точку обхода. <strong>После выбора элемента <span class="arithmatex">\(x_i\)</span> следующий раунд начинается с индекса <span class="arithmatex">\(i\)</span></strong>. Благодаря этому последовательность выборов всегда удовлетворяет условию <span class="arithmatex">\(i_1 \leq i_2 \leq \dots \leq i_m\)</span> , а значит, каждое подмножество создается только один раз.</p>
<p>Помимо этого, мы внесем в код еще два улучшения.</p>
<ul>
<li>Перед началом поиска отсортируем массив <code>nums</code> . Тогда при обходе всех вариантов <strong>можно сразу прервать цикл, как только сумма подмножества превысит <code>target</code></strong> , потому что все последующие элементы будут еще больше и их сумма тоже превысит <code>target</code> .</li>
<li>Откажемся от отдельной переменной суммы <code>total</code> и <strong>будем учитывать сумму через вычитание из <code>target</code></strong> ; когда <code>target</code> станет равным <span class="arithmatex">\(0\)</span> , решение фиксируется.</li>
<li>Откажемся от отдельной переменной суммы <code>total</code> и <strong>будем учитывать сумму через вычитание из <code>target</code></strong>. Когда <code>target</code> станет равным <span class="arithmatex">\(0\)</span> , решение фиксируется.</li>
</ul>
<div class="tabbed-set tabbed-alternate" data-tabs="2:13"><input checked="checked" id="__tabbed_2_1" name="__tabbed_2" type="radio" /><input id="__tabbed_2_2" name="__tabbed_2" type="radio" /><input id="__tabbed_2_3" name="__tabbed_2" type="radio" /><input id="__tabbed_2_4" name="__tabbed_2" type="radio" /><input id="__tabbed_2_5" name="__tabbed_2" type="radio" /><input id="__tabbed_2_6" name="__tabbed_2" type="radio" /><input id="__tabbed_2_7" name="__tabbed_2" type="radio" /><input id="__tabbed_2_8" name="__tabbed_2" type="radio" /><input id="__tabbed_2_9" name="__tabbed_2" type="radio" /><input id="__tabbed_2_10" name="__tabbed_2" type="radio" /><input id="__tabbed_2_11" name="__tabbed_2" type="radio" /><input id="__tabbed_2_12" name="__tabbed_2" type="radio" /><input id="__tabbed_2_13" name="__tabbed_2" type="radio" /><div class="tabbed-labels"><label for="__tabbed_2_1">Python</label><label for="__tabbed_2_2">C++</label><label for="__tabbed_2_3">Java</label><label for="__tabbed_2_4">C#</label><label for="__tabbed_2_5">Go</label><label for="__tabbed_2_6">Swift</label><label for="__tabbed_2_7">JS</label><label for="__tabbed_2_8">TS</label><label for="__tabbed_2_9">Dart</label><label for="__tabbed_2_10">Rust</label><label for="__tabbed_2_11">C</label><label for="__tabbed_2_12">Kotlin</label><label for="__tabbed_2_13">Ruby</label></div>
<div class="tabbed-content">
@@ -5526,10 +5526,10 @@
<h2 id="1332">13.3.2 &nbsp; Учет повторяющихся элементов<a class="headerlink" href="#1332" title="Permanent link">&para;</a></h2>
<div class="admonition question">
<p class="admonition-title">Question</p>
<p>Дан массив положительных целых чисел <code>nums</code> и целое положительное значение <code>target</code> . Найдите все возможные комбинации, сумма элементов которых равна <code>target</code> . <strong>Во входном массиве могут присутствовать повторяющиеся элементы, и каждый элемент разрешено выбирать только один раз</strong>. Верните эти комбинации в виде списка; в результате не должно быть повторяющихся комбинаций.</p>
<p>Дан массив положительных целых чисел <code>nums</code> и целое положительное значение <code>target</code> . Найдите все возможные комбинации, сумма элементов которых равна <code>target</code> . <strong>Во входном массиве могут присутствовать повторяющиеся элементы, и каждый элемент разрешено выбирать только один раз</strong>. Верните эти комбинации в виде списка. В результате не должно быть повторяющихся комбинаций.</p>
</div>
<p>По сравнению с предыдущей задачей <strong>во входном массиве теперь могут присутствовать повторяющиеся элементы</strong>, и это создает новую проблему. Например, если дан массив <span class="arithmatex">\([4, \hat{4}, 5]\)</span> и целевое значение <span class="arithmatex">\(9\)</span> , то существующий код вернет результат <span class="arithmatex">\([4, 5], [\hat{4}, 5]\)</span> , то есть с повторяющимся подмножеством.</p>
<p><strong>Причина появления дублей в том, что равные элементы выбираются несколько раз в одном и том же раунде</strong>. На рисунке 13-13 в первом раунде существует три варианта выбора, и два из них равны <span class="arithmatex">\(4\)</span> ; из-за этого появляются две дублирующиеся ветви поиска и, соответственно, повторяющиеся подмножества. Точно так же два элемента <span class="arithmatex">\(4\)</span> во втором раунде тоже порождают дубликаты.</p>
<p><strong>Причина появления дублей в том, что равные элементы выбираются несколько раз в одном и том же раунде</strong>. На рисунке 13-13 в первом раунде существует три варианта выбора, и два из них равны <span class="arithmatex">\(4\)</span>. Из-за этого появляются две дублирующиеся ветви поиска и, соответственно, повторяющиеся подмножества. Точно так же два элемента <span class="arithmatex">\(4\)</span> во втором раунде тоже порождают дубликаты.</p>
<p><img alt="Повторяющиеся подмножества из-за равных элементов" class="animation-figure" src="../subset_sum_problem.assets/subset_sum_ii_repeat.png" /></p>
<p align="center"> Рисунок 13-13 &nbsp; Повторяющиеся подмножества из-за равных элементов </p>