mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-19 15:00:58 +00:00
deploy
This commit is contained in:
@@ -3611,13 +3611,13 @@
|
||||
<h1 id="152-fractional-knapsack-problem">15.2 Fractional knapsack problem<a class="headerlink" href="#152-fractional-knapsack-problem" title="Permanent link">¶</a></h1>
|
||||
<div class="admonition question">
|
||||
<p class="admonition-title">Question</p>
|
||||
<p>Given <span class="arithmatex">\(n\)</span> items, the weight of the <span class="arithmatex">\(i\)</span>-th item is <span class="arithmatex">\(wgt[i-1]\)</span> and its value is <span class="arithmatex">\(val[i-1]\)</span>, and a knapsack with a capacity of <span class="arithmatex">\(cap\)</span>. Each item can be chosen only once, <strong>but a part of the item can be selected, with its value calculated based on the proportion of the weight chosen</strong>, what is the maximum value of the items in the knapsack under the limited capacity? An example is shown below.</p>
|
||||
<p>Given <span class="arithmatex">\(n\)</span> items, the weight of the <span class="arithmatex">\(i\)</span>-th item is <span class="arithmatex">\(wgt[i-1]\)</span> and its value is <span class="arithmatex">\(val[i-1]\)</span>, and a knapsack with a capacity of <span class="arithmatex">\(cap\)</span>. Each item can be chosen only once, <strong>but a part of the item can be selected, with its value calculated based on the proportion of the weight chosen</strong>, what is the maximum value of the items in the knapsack under the limited capacity? An example is shown in Figure 15-3.</p>
|
||||
</div>
|
||||
<p><a class="glightbox" href="../fractional_knapsack_problem.assets/fractional_knapsack_example.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Example data of the fractional knapsack problem" class="animation-figure" src="../fractional_knapsack_problem.assets/fractional_knapsack_example.png" /></a></p>
|
||||
<p align="center"> Figure 15-3 Example data of the fractional knapsack problem </p>
|
||||
|
||||
<p>The fractional knapsack problem is very similar overall to the 0-1 knapsack problem, involving the current item <span class="arithmatex">\(i\)</span> and capacity <span class="arithmatex">\(c\)</span>, aiming to maximize the value within the limited capacity of the knapsack.</p>
|
||||
<p>The difference is that, in this problem, only a part of an item can be chosen. As shown in the Figure 15-4 , <strong>we can arbitrarily split the items and calculate the corresponding value based on the weight proportion</strong>.</p>
|
||||
<p>The difference is that, in this problem, only a part of an item can be chosen. As shown in Figure 15-4, <strong>we can arbitrarily split the items and calculate the corresponding value based on the weight proportion</strong>.</p>
|
||||
<ol>
|
||||
<li>For item <span class="arithmatex">\(i\)</span>, its value per unit weight is <span class="arithmatex">\(val[i-1] / wgt[i-1]\)</span>, referred to as the unit value.</li>
|
||||
<li>Suppose we put a part of item <span class="arithmatex">\(i\)</span> with weight <span class="arithmatex">\(w\)</span> into the knapsack, then the value added to the knapsack is <span class="arithmatex">\(w \times val[i-1] / wgt[i-1]\)</span>.</li>
|
||||
@@ -3626,7 +3626,7 @@
|
||||
<p align="center"> Figure 15-4 Value per unit weight of the item </p>
|
||||
|
||||
<h3 id="1-greedy-strategy-determination">1. Greedy strategy determination<a class="headerlink" href="#1-greedy-strategy-determination" title="Permanent link">¶</a></h3>
|
||||
<p>Maximizing the total value of the items in the knapsack essentially means maximizing the value per unit weight. From this, the greedy strategy shown below can be deduced.</p>
|
||||
<p>Maximizing the total value of the items in the knapsack essentially means maximizing the value per unit weight. From this, the greedy strategy shown in Figure 15-5 can be deduced.</p>
|
||||
<ol>
|
||||
<li>Sort the items by their unit value from high to low.</li>
|
||||
<li>Iterate over all items, <strong>greedily choosing the item with the highest unit value in each round</strong>.</li>
|
||||
@@ -4094,7 +4094,7 @@
|
||||
<p>Using proof by contradiction. Suppose item <span class="arithmatex">\(x\)</span> has the highest unit value, and some algorithm yields a maximum value <code>res</code>, but the solution does not include item <span class="arithmatex">\(x\)</span>.</p>
|
||||
<p>Now remove a unit weight of any item from the knapsack and replace it with a unit weight of item <span class="arithmatex">\(x\)</span>. Since the unit value of item <span class="arithmatex">\(x\)</span> is the highest, the total value after replacement will definitely be greater than <code>res</code>. <strong>This contradicts the assumption that <code>res</code> is the optimal solution, proving that the optimal solution must include item <span class="arithmatex">\(x\)</span></strong>.</p>
|
||||
<p>For other items in this solution, we can also construct the above contradiction. Overall, <strong>items with greater unit value are always better choices</strong>, proving that the greedy strategy is effective.</p>
|
||||
<p>As shown in the Figure 15-6 , if the item weight and unit value are viewed as the horizontal and vertical axes of a two-dimensional chart respectively, the fractional knapsack problem can be transformed into "seeking the largest area enclosed within a limited horizontal axis range". This analogy can help us understand the effectiveness of the greedy strategy from a geometric perspective.</p>
|
||||
<p>As shown in Figure 15-6, if the item weight and unit value are viewed as the horizontal and vertical axes of a two-dimensional chart respectively, the fractional knapsack problem can be transformed into "seeking the largest area enclosed within a limited horizontal axis range". This analogy can help us understand the effectiveness of the greedy strategy from a geometric perspective.</p>
|
||||
<p><a class="glightbox" href="../fractional_knapsack_problem.assets/fractional_knapsack_area_chart.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Geometric representation of the fractional knapsack problem" class="animation-figure" src="../fractional_knapsack_problem.assets/fractional_knapsack_area_chart.png" /></a></p>
|
||||
<p align="center"> Figure 15-6 Geometric representation of the fractional knapsack problem </p>
|
||||
|
||||
|
||||
@@ -3638,7 +3638,7 @@
|
||||
<p class="admonition-title">Question</p>
|
||||
<p>Given <span class="arithmatex">\(n\)</span> types of coins, where the denomination of the <span class="arithmatex">\(i\)</span>th type of coin is <span class="arithmatex">\(coins[i - 1]\)</span>, and the target amount is <span class="arithmatex">\(amt\)</span>, with each type of coin available indefinitely, what is the minimum number of coins needed to make up the target amount? If it is not possible to make up the target amount, return <span class="arithmatex">\(-1\)</span>.</p>
|
||||
</div>
|
||||
<p>The greedy strategy adopted in this problem is shown in the following figure. Given the target amount, <strong>we greedily choose the coin that is closest to and not greater than it</strong>, repeatedly following this step until the target amount is met.</p>
|
||||
<p>The greedy strategy adopted in this problem is shown in Figure 15-1. Given the target amount, <strong>we greedily choose the coin that is closest to and not greater than it</strong>, repeatedly following this step until the target amount is met.</p>
|
||||
<p><a class="glightbox" href="../greedy_algorithm.assets/coin_change_greedy_strategy.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Greedy strategy for coin change" class="animation-figure" src="../greedy_algorithm.assets/coin_change_greedy_strategy.png" /></a></p>
|
||||
<p align="center"> Figure 15-1 Greedy strategy for coin change </p>
|
||||
|
||||
@@ -3921,7 +3921,7 @@
|
||||
<p>You might exclaim: So clean! The greedy algorithm solves the coin change problem in about ten lines of code.</p>
|
||||
<h2 id="1511-advantages-and-limitations-of-greedy-algorithms">15.1.1 Advantages and limitations of greedy algorithms<a class="headerlink" href="#1511-advantages-and-limitations-of-greedy-algorithms" title="Permanent link">¶</a></h2>
|
||||
<p><strong>Greedy algorithms are not only straightforward and simple to implement, but they are also usually very efficient</strong>. In the code above, if the smallest coin denomination is <span class="arithmatex">\(\min(coins)\)</span>, the greedy choice loops at most <span class="arithmatex">\(amt / \min(coins)\)</span> times, giving a time complexity of <span class="arithmatex">\(O(amt / \min(coins))\)</span>. This is an order of magnitude smaller than the time complexity of the dynamic programming solution, which is <span class="arithmatex">\(O(n \times amt)\)</span>.</p>
|
||||
<p>However, <strong>for some combinations of coin denominations, greedy algorithms cannot find the optimal solution</strong>. The following figure provides two examples.</p>
|
||||
<p>However, <strong>for some combinations of coin denominations, greedy algorithms cannot find the optimal solution</strong>. Figure 15-2 provides two examples.</p>
|
||||
<ul>
|
||||
<li><strong>Positive example <span class="arithmatex">\(coins = [1, 5, 10, 20, 50, 100]\)</span></strong>: In this coin combination, given any <span class="arithmatex">\(amt\)</span>, the greedy algorithm can find the optimal solution.</li>
|
||||
<li><strong>Negative example <span class="arithmatex">\(coins = [1, 20, 50]\)</span></strong>: Suppose <span class="arithmatex">\(amt = 60\)</span>, the greedy algorithm can only find the combination <span class="arithmatex">\(50 + 1 \times 10\)</span>, totaling 11 coins, but dynamic programming can find the optimal solution of <span class="arithmatex">\(20 + 20 + 20\)</span>, needing only 3 coins.</li>
|
||||
|
||||
@@ -3613,7 +3613,7 @@
|
||||
<p class="admonition-title">Question</p>
|
||||
<p>Input an array <span class="arithmatex">\(ht\)</span>, where each element represents the height of a vertical partition. Any two partitions in the array, along with the space between them, can form a container.</p>
|
||||
<p>The capacity of the container is the product of the height and the width (area), where the height is determined by the shorter partition, and the width is the difference in array indices between the two partitions.</p>
|
||||
<p>Please select two partitions in the array that maximize the container's capacity and return this maximum capacity. An example is shown in the following figure.</p>
|
||||
<p>Please select two partitions in the array that maximize the container's capacity and return this maximum capacity. An example is shown in Figure 15-7.</p>
|
||||
</div>
|
||||
<p><a class="glightbox" href="../max_capacity_problem.assets/max_capacity_example.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Example data for the maximum capacity problem" class="animation-figure" src="../max_capacity_problem.assets/max_capacity_example.png" /></a></p>
|
||||
<p align="center"> Figure 15-7 Example data for the maximum capacity problem </p>
|
||||
@@ -3625,21 +3625,21 @@ cap[i, j] = \min(ht[i], ht[j]) \times (j - i)
|
||||
\]</div>
|
||||
<p>Assuming the length of the array is <span class="arithmatex">\(n\)</span>, the number of combinations of two partitions (total number of states) is <span class="arithmatex">\(C_n^2 = \frac{n(n - 1)}{2}\)</span>. The most straightforward approach is to <strong>enumerate all possible states</strong>, resulting in a time complexity of <span class="arithmatex">\(O(n^2)\)</span>.</p>
|
||||
<h3 id="1-determination-of-a-greedy-strategy">1. Determination of a greedy strategy<a class="headerlink" href="#1-determination-of-a-greedy-strategy" title="Permanent link">¶</a></h3>
|
||||
<p>There is a more efficient solution to this problem. As shown in the following figure, we select a state <span class="arithmatex">\([i, j]\)</span> where the indices <span class="arithmatex">\(i < j\)</span> and the height <span class="arithmatex">\(ht[i] < ht[j]\)</span>, meaning <span class="arithmatex">\(i\)</span> is the shorter partition, and <span class="arithmatex">\(j\)</span> is the taller one.</p>
|
||||
<p>There is a more efficient solution to this problem. As shown in Figure 15-8, we select a state <span class="arithmatex">\([i, j]\)</span> where the indices <span class="arithmatex">\(i < j\)</span> and the height <span class="arithmatex">\(ht[i] < ht[j]\)</span>, meaning <span class="arithmatex">\(i\)</span> is the shorter partition, and <span class="arithmatex">\(j\)</span> is the taller one.</p>
|
||||
<p><a class="glightbox" href="../max_capacity_problem.assets/max_capacity_initial_state.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Initial state" class="animation-figure" src="../max_capacity_problem.assets/max_capacity_initial_state.png" /></a></p>
|
||||
<p align="center"> Figure 15-8 Initial state </p>
|
||||
|
||||
<p>As shown in the following figure, <strong>if we move the taller partition <span class="arithmatex">\(j\)</span> closer to the shorter partition <span class="arithmatex">\(i\)</span>, the capacity will definitely decrease</strong>.</p>
|
||||
<p>As shown in Figure 15-9, <strong>if we move the taller partition <span class="arithmatex">\(j\)</span> closer to the shorter partition <span class="arithmatex">\(i\)</span>, the capacity will definitely decrease</strong>.</p>
|
||||
<p>This is because when moving the taller partition <span class="arithmatex">\(j\)</span>, the width <span class="arithmatex">\(j-i\)</span> definitely decreases; and since the height is determined by the shorter partition, the height can only remain the same (if <span class="arithmatex">\(i\)</span> remains the shorter partition) or decrease (if the moved <span class="arithmatex">\(j\)</span> becomes the shorter partition).</p>
|
||||
<p><a class="glightbox" href="../max_capacity_problem.assets/max_capacity_moving_long_board.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="State after moving the taller partition inward" class="animation-figure" src="../max_capacity_problem.assets/max_capacity_moving_long_board.png" /></a></p>
|
||||
<p align="center"> Figure 15-9 State after moving the taller partition inward </p>
|
||||
|
||||
<p>Conversely, <strong>we can only possibly increase the capacity by moving the shorter partition <span class="arithmatex">\(i\)</span> inward</strong>. Although the width will definitely decrease, <strong>the height may increase</strong> (if the moved shorter partition <span class="arithmatex">\(i\)</span> becomes taller). For example, in the Figure 15-10 , the area increases after moving the shorter partition.</p>
|
||||
<p>Conversely, <strong>we can only possibly increase the capacity by moving the shorter partition <span class="arithmatex">\(i\)</span> inward</strong>. Although the width will definitely decrease, <strong>the height may increase</strong> (if the moved shorter partition <span class="arithmatex">\(i\)</span> becomes taller). For example, in Figure 15-10, the area increases after moving the shorter partition.</p>
|
||||
<p><a class="glightbox" href="../max_capacity_problem.assets/max_capacity_moving_short_board.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="State after moving the shorter partition inward" class="animation-figure" src="../max_capacity_problem.assets/max_capacity_moving_short_board.png" /></a></p>
|
||||
<p align="center"> Figure 15-10 State after moving the shorter partition inward </p>
|
||||
|
||||
<p>This leads us to the greedy strategy for this problem: initialize two pointers at the ends of the container, and in each round, move the pointer corresponding to the shorter partition inward until the two pointers meet.</p>
|
||||
<p>The following figures illustrate the execution of the greedy strategy.</p>
|
||||
<p>Figure 15-11 illustrate the execution of the greedy strategy.</p>
|
||||
<ol>
|
||||
<li>Initially, the pointers <span class="arithmatex">\(i\)</span> and <span class="arithmatex">\(j\)</span> are positioned at the ends of the array.</li>
|
||||
<li>Calculate the current state's capacity <span class="arithmatex">\(cap[i, j]\)</span> and update the maximum capacity.</li>
|
||||
@@ -3979,7 +3979,7 @@ cap[i, j] = \min(ht[i], ht[j]) \times (j - i)
|
||||
</details>
|
||||
<h3 id="3-proof-of-correctness">3. Proof of correctness<a class="headerlink" href="#3-proof-of-correctness" title="Permanent link">¶</a></h3>
|
||||
<p>The reason why the greedy method is faster than enumeration is that each round of greedy selection "skips" some states.</p>
|
||||
<p>For example, under the state <span class="arithmatex">\(cap[i, j]\)</span> where <span class="arithmatex">\(i\)</span> is the shorter partition and <span class="arithmatex">\(j\)</span> is the taller partition, greedily moving the shorter partition <span class="arithmatex">\(i\)</span> inward by one step leads to the "skipped" states shown below. <strong>This means that these states' capacities cannot be verified later</strong>.</p>
|
||||
<p>For example, under the state <span class="arithmatex">\(cap[i, j]\)</span> where <span class="arithmatex">\(i\)</span> is the shorter partition and <span class="arithmatex">\(j\)</span> is the taller partition, greedily moving the shorter partition <span class="arithmatex">\(i\)</span> inward by one step leads to the "skipped" states shown in Figure 15-12. <strong>This means that these states' capacities cannot be verified later</strong>.</p>
|
||||
<div class="arithmatex">\[
|
||||
cap[i, i+1], cap[i, i+2], \dots, cap[i, j-2], cap[i, j-1]
|
||||
\]</div>
|
||||
|
||||
@@ -3634,13 +3634,13 @@ n = \sum_{i=1}^{m}n_i
|
||||
n & \geq 4
|
||||
\end{aligned}
|
||||
\]</div>
|
||||
<p>As shown below, when <span class="arithmatex">\(n \geq 4\)</span>, splitting out a <span class="arithmatex">\(2\)</span> increases the product, <strong>which indicates that integers greater than or equal to <span class="arithmatex">\(4\)</span> should be split</strong>.</p>
|
||||
<p>As shown in Figure 15-14, when <span class="arithmatex">\(n \geq 4\)</span>, splitting out a <span class="arithmatex">\(2\)</span> increases the product, <strong>which indicates that integers greater than or equal to <span class="arithmatex">\(4\)</span> should be split</strong>.</p>
|
||||
<p><strong>Greedy strategy one</strong>: If the splitting scheme includes factors <span class="arithmatex">\(\geq 4\)</span>, they should be further split. The final split should only include factors <span class="arithmatex">\(1\)</span>, <span class="arithmatex">\(2\)</span>, and <span class="arithmatex">\(3\)</span>.</p>
|
||||
<p><a class="glightbox" href="../max_product_cutting_problem.assets/max_product_cutting_greedy_infer1.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Product increase due to splitting" class="animation-figure" src="../max_product_cutting_problem.assets/max_product_cutting_greedy_infer1.png" /></a></p>
|
||||
<p align="center"> Figure 15-14 Product increase due to splitting </p>
|
||||
|
||||
<p>Next, consider which factor is optimal. Among the factors <span class="arithmatex">\(1\)</span>, <span class="arithmatex">\(2\)</span>, and <span class="arithmatex">\(3\)</span>, clearly <span class="arithmatex">\(1\)</span> is the worst, as <span class="arithmatex">\(1 \times (n-1) < n\)</span> always holds, meaning splitting out <span class="arithmatex">\(1\)</span> actually decreases the product.</p>
|
||||
<p>As shown below, when <span class="arithmatex">\(n = 6\)</span>, <span class="arithmatex">\(3 \times 3 > 2 \times 2 \times 2\)</span>. <strong>This means splitting out <span class="arithmatex">\(3\)</span> is better than splitting out <span class="arithmatex">\(2\)</span></strong>.</p>
|
||||
<p>As shown in Figure 15-15, when <span class="arithmatex">\(n = 6\)</span>, <span class="arithmatex">\(3 \times 3 > 2 \times 2 \times 2\)</span>. <strong>This means splitting out <span class="arithmatex">\(3\)</span> is better than splitting out <span class="arithmatex">\(2\)</span></strong>.</p>
|
||||
<p><strong>Greedy strategy two</strong>: In the splitting scheme, there should be at most two <span class="arithmatex">\(2\)</span>s. Because three <span class="arithmatex">\(2\)</span>s can always be replaced by two <span class="arithmatex">\(3\)</span>s to obtain a higher product.</p>
|
||||
<p><a class="glightbox" href="../max_product_cutting_problem.assets/max_product_cutting_greedy_infer2.png" data-type="image" data-width="100%" data-height="auto" data-desc-position="bottom"><img alt="Optimal splitting factors" class="animation-figure" src="../max_product_cutting_problem.assets/max_product_cutting_greedy_infer2.png" /></a></p>
|
||||
<p align="center"> Figure 15-15 Optimal splitting factors </p>
|
||||
@@ -3653,7 +3653,7 @@ n & \geq 4
|
||||
<li>When the remainder is <span class="arithmatex">\(1\)</span>, since <span class="arithmatex">\(2 \times 2 > 1 \times 3\)</span>, the last <span class="arithmatex">\(3\)</span> should be replaced with <span class="arithmatex">\(2\)</span>.</li>
|
||||
</ol>
|
||||
<h3 id="2-code-implementation">2. Code implementation<a class="headerlink" href="#2-code-implementation" title="Permanent link">¶</a></h3>
|
||||
<p>As shown below, we do not need to use loops to split the integer but can use the floor division operation to get the number of <span class="arithmatex">\(3\)</span>s, <span class="arithmatex">\(a\)</span>, and the modulo operation to get the remainder, <span class="arithmatex">\(b\)</span>, thus:</p>
|
||||
<p>As shown in Figure 15-16, we do not need to use loops to split the integer but can use the floor division operation to get the number of <span class="arithmatex">\(3\)</span>s, <span class="arithmatex">\(a\)</span>, and the modulo operation to get the remainder, <span class="arithmatex">\(b\)</span>, thus:</p>
|
||||
<div class="arithmatex">\[
|
||||
n = 3a + b
|
||||
\]</div>
|
||||
|
||||
Reference in New Issue
Block a user