This commit is contained in:
krahets
2026-04-02 03:08:50 +08:00
parent 09a136c9fa
commit aaf9f58eb3
157 changed files with 3002 additions and 2994 deletions
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -4194,7 +4194,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -4381,13 +4381,13 @@
<h1 id="152-fractional-knapsack-problem">15.2 &nbsp; Fractional Knapsack Problem<a class="headerlink" href="#152-fractional-knapsack-problem" title="Permanent link">&para;</a></h1>
<div class="admonition question">
<p class="admonition-title">Question</p>
<p>Given <span class="arithmatex">\(n\)</span> items, where 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 capacity <span class="arithmatex">\(cap\)</span>. Each item can be selected only once, <strong>but a portion of an item can be selected, with the value calculated based on the proportion of weight selected</strong>, what is the maximum value of items in the knapsack under the limited capacity? An example is shown in Figure 15-3.</p>
<p>Given <span class="arithmatex">\(n\)</span> items, where 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 capacity <span class="arithmatex">\(cap\)</span>. Each item can be selected only once, <strong>but a fraction of an item may be selected, with its value proportional to the selected weight</strong>. What is the maximum total value that can be placed in the knapsack under the capacity constraint? An example is shown in Figure 15-3.</p>
</div>
<p><img alt="Example data for the fractional knapsack problem" class="animation-figure" src="../fractional_knapsack_problem.assets/fractional_knapsack_example.png" /></p>
<p align="center"> Figure 15-3 &nbsp; Example data for the fractional knapsack problem </p>
<p>The fractional knapsack problem is very similar overall to the 0-1 knapsack problem, with states including the current item <span class="arithmatex">\(i\)</span> and capacity <span class="arithmatex">\(c\)</span>, and the goal being to maximize value under the limited knapsack capacity.</p>
<p>The difference is that this problem allows selecting only a portion of an item. As shown in Figure 15-4, <strong>we can arbitrarily split items and calculate the corresponding value based on the weight proportion</strong>.</p>
<p>The difference is that this problem allows selecting only a fraction of an item. As shown in Figure 15-4, <strong>we can split an item arbitrarily and compute its value in proportion to the selected weight</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 unit value.</li>
<li>Suppose we put a portion 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>
@@ -4396,7 +4396,7 @@
<p align="center"> Figure 15-4 &nbsp; Value of items per unit weight </p>
<h3 id="1-greedy-strategy-determination">1. &nbsp; Greedy Strategy Determination<a class="headerlink" href="#1-greedy-strategy-determination" title="Permanent link">&para;</a></h3>
<p>Maximizing the total value of items in the knapsack <strong>is essentially maximizing the value per unit weight of items</strong>. From this, we can derive the greedy strategy shown in Figure 15-5.</p>
<p>Maximizing the total value in the knapsack <strong>essentially means prioritizing items with higher value per unit weight</strong>. From this observation, we can derive the greedy strategy shown in Figure 15-5.</p>
<ol>
<li>Sort items by unit value from high to low.</li>
<li>Iterate through all items, <strong>greedily selecting the item with the highest unit value in each round</strong>.</li>
@@ -4406,7 +4406,7 @@
<p align="center"> Figure 15-5 &nbsp; Greedy strategy for the fractional knapsack problem </p>
<h3 id="2-code-implementation">2. &nbsp; Code Implementation<a class="headerlink" href="#2-code-implementation" title="Permanent link">&para;</a></h3>
<p>We created an <code>Item</code> class to facilitate sorting items by unit value. We loop to make greedy selections, breaking when the knapsack is full and returning the solution:</p>
<p>We define an <code>Item</code> class so that items can be sorted by unit value. We then iterate through the sorted items greedily, stopping once the knapsack is full and returning the result:</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">
<div class="tabbed-block">
@@ -4877,14 +4877,14 @@
</div>
</div>
</div>
<p>The time complexity of built-in sorting algorithms is usually <span class="arithmatex">\(O(\log n)\)</span>, and the space complexity is usually <span class="arithmatex">\(O(\log n)\)</span> or <span class="arithmatex">\(O(n)\)</span>, depending on the specific implementation of the programming language.</p>
<p>Built-in sorting algorithms usually take <span class="arithmatex">\(O(n \log n)\)</span> time, and their space complexity is usually <span class="arithmatex">\(O(\log n)\)</span> or <span class="arithmatex">\(O(n)\)</span>, depending on the specific implementation of the programming language.</p>
<p>Apart from sorting, in the worst case the entire item list needs to be traversed, <strong>therefore the time complexity is <span class="arithmatex">\(O(n)\)</span></strong>, where <span class="arithmatex">\(n\)</span> is the number of items.</p>
<p>Since an <code>Item</code> object list is initialized, <strong>the space complexity is <span class="arithmatex">\(O(n)\)</span></strong>.</p>
<h3 id="3-correctness-proof">3. &nbsp; Correctness Proof<a class="headerlink" href="#3-correctness-proof" title="Permanent link">&para;</a></h3>
<p>Using proof by contradiction. Suppose item <span class="arithmatex">\(x\)</span> has the highest unit value, and some algorithm yields a maximum value of <code>res</code>, but this 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 item <span class="arithmatex">\(x\)</span> has the highest unit value, 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. In summary, <strong>items with greater unit value are always better choices</strong>, which proves that the greedy strategy is effective.</p>
<p>As shown in Figure 15-6, if we view item weight and item unit value as the horizontal and vertical axes of a two-dimensional chart respectively, then the fractional knapsack problem can be transformed into "finding the maximum 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>We use proof by contradiction. Suppose item <span class="arithmatex">\(x\)</span> has the highest unit value, and some algorithm produces an optimal value <code>res</code>, but the resulting solution does not include item <span class="arithmatex">\(x\)</span>.</p>
<p>Now remove one unit of weight from any item in the knapsack and replace it with one unit of weight from item <span class="arithmatex">\(x\)</span>. Since item <span class="arithmatex">\(x\)</span> has the highest unit value, the total value after the replacement must be greater than <code>res</code>. <strong>This contradicts the assumption that <code>res</code> is optimal, proving that any optimal solution must include item <span class="arithmatex">\(x\)</span></strong>.</p>
<p>We can construct the same contradiction for the other items in the solution as well. In summary, <strong>items with higher unit value are always the better choice</strong>, which proves that the greedy strategy is effective.</p>
<p>As shown in Figure 15-6, if we treat item weight and unit value as the horizontal and vertical axes of a two-dimensional chart, then the fractional knapsack problem can be viewed as "finding the maximum area enclosed within a bounded interval on the horizontal axis." This analogy helps explain the effectiveness of the greedy strategy from a geometric perspective.</p>
<p><img alt="Geometric representation of the fractional knapsack problem" class="animation-figure" src="../fractional_knapsack_problem.assets/fractional_knapsack_area_chart.png" /></p>
<p align="center"> Figure 15-6 &nbsp; Geometric representation of the fractional knapsack problem </p>
+45 -45
View File
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -4205,7 +4205,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -4401,18 +4401,18 @@
<!-- Page content -->
<h1 id="151-greedy-algorithm">15.1 &nbsp; Greedy Algorithm<a class="headerlink" href="#151-greedy-algorithm" title="Permanent link">&para;</a></h1>
<p><u>Greedy algorithm</u> is a common algorithm for solving optimization problems. Its basic idea is to make the seemingly best choice at each decision stage of the problem, that is, to greedily make locally optimal decisions in hopes of obtaining a globally optimal solution. Greedy algorithms are simple and efficient, and are widely applied in many practical problems.</p>
<p><u>Greedy algorithm</u> is a common approach to solving optimization problems. Its basic idea is to choose the option that appears best at each decision stage, that is, to greedily make locally optimal decisions in the hope of obtaining a globally optimal solution. Greedy algorithms are simple and efficient, and are widely used in many practical problems.</p>
<p>Greedy algorithms and dynamic programming are both commonly used to solve optimization problems. They share some similarities, such as both relying on the optimal substructure property, but they work differently.</p>
<ul>
<li>Dynamic programming considers all previous decisions when making the current decision, and uses solutions to past subproblems to construct the solution to the current subproblem.</li>
<li>Greedy algorithms do not consider past decisions, but instead make greedy choices moving forward, continually reducing the problem size until the problem is solved.</li>
</ul>
<p>We will first understand how greedy algorithms work through the example problem "coin change". This problem has already been introduced in the "Complete Knapsack Problem" chapter, so I believe you are not unfamiliar with it.</p>
<p>We will first understand how greedy algorithms work through the example problem "coin change." This problem was already introduced in the "Complete Knapsack Problem" chapter, so it should already be familiar to you.</p>
<div class="admonition question">
<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 for repeated selection, what is the minimum number of coins needed to make up the target amount? If it is impossible to make up the target amount, return <span class="arithmatex">\(-1\)</span>.</p>
<p>Given <span class="arithmatex">\(n\)</span> types of coins, where the denomination of the <span class="arithmatex">\(i\)</span>-th type is <span class="arithmatex">\(coins[i - 1]\)</span>, a target amount <span class="arithmatex">\(amt\)</span>, and an unlimited number of coins of each type, what is the minimum number of coins needed to make up the target amount? If the target amount cannot be made up, return <span class="arithmatex">\(-1\)</span>.</p>
</div>
<p>The greedy strategy adopted for this problem is shown in Figure 15-1. Given a target amount, <strong>we greedily select the coin that is not greater than and closest to it</strong>, and continuously repeat this step until the target amount is reached.</p>
<p>The greedy strategy for this problem is shown in Figure 15-1. Given a target amount, <strong>we greedily choose the coin that does not exceed it and is closest to it</strong>, repeating this step until the target amount is made up.</p>
<p><img alt="Greedy strategy for coin change" class="animation-figure" src="../greedy_algorithm.assets/coin_change_greedy_strategy.png" /></p>
<p align="center"> Figure 15-1 &nbsp; Greedy strategy for coin change </p>
@@ -4700,23 +4700,23 @@
</div>
</div>
</div>
<p>You might exclaim: So clean! The greedy algorithm solves the coin change problem in about ten lines of code.</p>
<p>You may find yourself exclaiming, "So clean!" The greedy algorithm solves the coin change problem in only about ten lines of code.</p>
<h2 id="1511-advantages-and-limitations-of-greedy-algorithms">15.1.1 &nbsp; Advantages and Limitations of Greedy Algorithms<a class="headerlink" href="#1511-advantages-and-limitations-of-greedy-algorithms" title="Permanent link">&para;</a></h2>
<p><strong>Greedy algorithms are not only straightforward and simple to implement, but 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 <span class="arithmatex">\(O(n \times amt)\)</span>.</p>
<p>However, <strong>for certain coin denomination combinations, greedy algorithms cannot find the optimal solution</strong>. Figure 15-2 provides two examples.</p>
<p><strong>Greedy algorithms are not only straightforward to apply and easy to implement, but are also usually very efficient</strong>. In the code above, if the smallest coin denomination is <span class="arithmatex">\(\min(coins)\)</span>, the greedy selection loop runs 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 lower than the time complexity of the dynamic programming solution, <span class="arithmatex">\(O(n \times amt)\)</span>.</p>
<p>However, <strong>for some coin denomination sets, greedy algorithms cannot find the optimal solution</strong>. Figure 15-2 shows two examples.</p>
<ul>
<li><strong>Positive example <span class="arithmatex">\(coins = [1, 5, 10, 20, 50, 100]\)</span></strong>: With 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 <span class="arithmatex">\(11\)</span> coins, but dynamic programming can find the optimal solution <span class="arithmatex">\(20 + 20 + 20\)</span>, requiring only <span class="arithmatex">\(3\)</span> coins.</li>
<li><strong>Negative example <span class="arithmatex">\(coins = [1, 49, 50]\)</span></strong>: Suppose <span class="arithmatex">\(amt = 98\)</span>, the greedy algorithm can only find the combination <span class="arithmatex">\(50 + 1 \times 48\)</span>, totaling <span class="arithmatex">\(49\)</span> coins, but dynamic programming can find the optimal solution <span class="arithmatex">\(49 + 49\)</span>, requiring only <span class="arithmatex">\(2\)</span> coins.</li>
<li><strong>Positive example <span class="arithmatex">\(coins = [1, 5, 10, 20, 50, 100]\)</span></strong>: With this coin set, the greedy algorithm can find the optimal solution for any <span class="arithmatex">\(amt\)</span>.</li>
<li><strong>Counterexample <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>, using <span class="arithmatex">\(11\)</span> coins in total, whereas dynamic programming can find the optimal solution <span class="arithmatex">\(20 + 20 + 20\)</span> using only <span class="arithmatex">\(3\)</span> coins.</li>
<li><strong>Counterexample <span class="arithmatex">\(coins = [1, 49, 50]\)</span></strong>: Suppose <span class="arithmatex">\(amt = 98\)</span>. The greedy algorithm can only find the combination <span class="arithmatex">\(50 + 1 \times 48\)</span>, using <span class="arithmatex">\(49\)</span> coins in total, whereas dynamic programming can find the optimal solution <span class="arithmatex">\(49 + 49\)</span> using only <span class="arithmatex">\(2\)</span> coins.</li>
</ul>
<p><img alt="Examples where greedy algorithms cannot find the optimal solution" class="animation-figure" src="../greedy_algorithm.assets/coin_change_greedy_vs_dp.png" /></p>
<p align="center"> Figure 15-2 &nbsp; Examples where greedy algorithms cannot find the optimal solution </p>
<p>In other words, for the coin change problem, greedy algorithms cannot guarantee finding the global optimal solution, and may even find very poor solutions. It is better suited for solving with dynamic programming.</p>
<p>Generally, the applicability of greedy algorithms falls into the following two situations.</p>
<p>In other words, for the coin change problem, greedy algorithms cannot guarantee a globally optimal solution and may even produce very poor results. This problem is better solved with dynamic programming.</p>
<p>In general, greedy algorithms are applicable in the following two situations.</p>
<ol>
<li><strong>Can guarantee finding the optimal solution</strong>: In this situation, greedy algorithms are often the best choice, because they tend to be more efficient than backtracking and dynamic programming.</li>
<li><strong>Can find an approximate optimal solution</strong>: Greedy algorithms are also applicable in this situation. For many complex problems, finding the global optimal solution is very difficult, and being able to find a suboptimal solution with high efficiency is also very good.</li>
<li><strong>The optimal solution can be guaranteed</strong>: In this case, greedy algorithms are often the best choice because they tend to be more efficient than backtracking and dynamic programming.</li>
<li><strong>An approximately optimal solution can be found</strong>: Greedy algorithms are also useful in this case. For many complex problems, finding the global optimal solution is very difficult, so efficiently finding a suboptimal solution is already a very good outcome.</li>
</ol>
<h2 id="1512-characteristics-of-greedy-algorithms">15.1.2 &nbsp; Characteristics of Greedy Algorithms<a class="headerlink" href="#1512-characteristics-of-greedy-algorithms" title="Permanent link">&para;</a></h2>
<p>So the question arises: what kind of problems are suitable for solving with greedy algorithms? Or in other words, under what conditions can greedy algorithms guarantee finding the optimal solution?</p>
@@ -4727,26 +4727,26 @@
</ul>
<p>Optimal substructure has already been introduced in the "Dynamic Programming" chapter, so we won't elaborate on it here. It's worth noting that the optimal substructure of some problems is not obvious, but they can still be solved using greedy algorithms.</p>
<p>We mainly explore methods for determining the greedy choice property. Although its description seems relatively simple, <strong>in practice, for many problems, proving the greedy choice property is not easy</strong>.</p>
<p>For example, in the coin change problem, although we can easily provide counterexamples to disprove the greedy choice property, proving it is quite difficult. If asked: <strong>what conditions must a coin combination satisfy to be solvable using a greedy algorithm</strong>? We often can only rely on intuition or examples to give an ambiguous answer, and find it difficult to provide a rigorous mathematical proof.</p>
<p>For example, in the coin change problem, although we can easily provide counterexamples to disprove the greedy choice property, proving that it holds is much harder. If asked, <strong>under what conditions can a coin set be solved using a greedy algorithm</strong>? We often can only rely on intuition or examples to give a vague answer, and it is difficult to provide a rigorous mathematical proof.</p>
<div class="admonition quote">
<p class="admonition-title">Quote</p>
<p>There is a paper that presents an algorithm with <span class="arithmatex">\(O(n^3)\)</span> time complexity for determining whether a coin combination can use a greedy algorithm to find the optimal solution for any amount.</p>
<p>There is a paper that presents an <span class="arithmatex">\(O(n^3)\)</span> algorithm for determining whether a coin set can be solved optimally by a greedy algorithm for any amount.</p>
<p>Pearson, D. A polynomial-time algorithm for the change-making problem[J]. Operations Research Letters, 2005, 33(3): 231-234.</p>
</div>
<h2 id="1513-steps-for-solving-problems-with-greedy-algorithms">15.1.3 &nbsp; Steps for Solving Problems with Greedy Algorithms<a class="headerlink" href="#1513-steps-for-solving-problems-with-greedy-algorithms" title="Permanent link">&para;</a></h2>
<p>The problem-solving process for greedy problems can generally be divided into the following three steps.</p>
<p>The general process for solving greedy problems can be divided into the following three steps.</p>
<ol>
<li><strong>Problem analysis</strong>: Sort out and understand the problem characteristics, including state definition, optimization objectives, and constraints, etc. This step is also involved in backtracking and dynamic programming.</li>
<li><strong>Determine the greedy strategy</strong>: Determine how to make greedy choices at each step. This strategy should be able to reduce the problem size at each step, ultimately solving the entire problem.</li>
<li><strong>Correctness proof</strong>: It is usually necessary to prove that the problem has both greedy choice property and optimal substructure. This step may require mathematical proofs, such as mathematical induction or proof by contradiction.</li>
<li><strong>Problem analysis</strong>: Sort out and understand the characteristics of the problem, including state definitions, optimization objectives, and constraints. This step also appears in backtracking and dynamic programming.</li>
<li><strong>Determine the greedy strategy</strong>: Decide how to make a greedy choice at each step. This strategy should reduce the problem size step by step and ultimately solve the entire problem.</li>
<li><strong>Correctness proof</strong>: It is usually necessary to prove that the problem has both greedy choice property and optimal substructure. This step may require mathematical tools such as induction or proof by contradiction.</li>
</ol>
<p>Determining the greedy strategy is the core step in solving the problem, but it may not be easy to implement, mainly for the following reasons.</p>
<p>Determining the greedy strategy is the core step in solving such problems, but it may not be easy in practice, mainly for the following reasons.</p>
<ul>
<li><strong>Greedy strategies differ greatly between different problems</strong>. For many problems, the greedy strategy is relatively straightforward, and we can derive it through some general thinking and attempts. However, for some complex problems, the greedy strategy may be very elusive, which really tests one's problem-solving experience and algorithmic ability.</li>
<li><strong>Some greedy strategies are highly misleading</strong>. When we confidently design a greedy strategy, write the solution code and submit it for testing, we may find that some test cases cannot pass. This is because the designed greedy strategy is only "partially correct", as exemplified by the coin change problem discussed above.</li>
<li><strong>Greedy strategies vary greatly from problem to problem</strong>. For many problems, the greedy strategy is fairly intuitive and can be derived through rough reasoning and experimentation. For some complex problems, however, the greedy strategy may be deeply hidden, which strongly tests one's problem-solving experience and algorithmic ability.</li>
<li><strong>Some greedy strategies are highly deceptive</strong>. We may confidently design a greedy strategy, write the solution code, and submit it, only to find that some test cases fail. This is because the designed greedy strategy is only "partially correct," as exemplified by the coin change problem discussed above.</li>
</ul>
<p>To ensure correctness, we should rigorously mathematically prove the greedy strategy, <strong>usually using proof by contradiction or mathematical induction</strong>.</p>
<p>However, correctness proofs may also not be easy. If we have no clue, we usually choose to debug the code based on test cases, step by step modifying and verifying the greedy strategy.</p>
<p>To ensure correctness, we should give a rigorous mathematical proof of the greedy strategy, <strong>usually using proof by contradiction or mathematical induction</strong>.</p>
<p>However, correctness proofs can also be difficult. If we have no clear direction, we usually resort to debugging against test cases, revising and validating the greedy strategy step by step.</p>
<h2 id="1514-typical-problems-solved-by-greedy-algorithms">15.1.4 &nbsp; Typical Problems Solved by Greedy Algorithms<a class="headerlink" href="#1514-typical-problems-solved-by-greedy-algorithms" title="Permanent link">&para;</a></h2>
<p>Greedy algorithms are often applied to optimization problems that satisfy greedy choice property and optimal substructure. Below are some typical greedy algorithm problems.</p>
<ul>
+22 -22
View File
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -4113,7 +4113,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -4279,8 +4279,8 @@
<p><img alt="Greedy" class="cover-image" src="../assets/covers/chapter_greedy.jpg" /></p>
<div class="admonition abstract">
<p class="admonition-title">Abstract</p>
<p>Sunflowers turn toward the sun, constantly pursuing the maximum potential for their own growth.</p>
<p>Through rounds of simple choices, greedy strategies gradually lead to the best answer.</p>
<p>Sunflowers turn toward the sun, always seeking the fullest growth possible.</p>
<p>Through successive simple choices, greedy strategies gradually lead to the optimal solution.</p>
</div>
<h2 id="chapter-contents">Chapter contents<a class="headerlink" href="#chapter-contents" title="Permanent link">&para;</a></h2>
<ul>
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -4194,7 +4194,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -4381,40 +4381,40 @@
<h1 id="153-max-capacity-problem">15.3 &nbsp; Max Capacity Problem<a class="headerlink" href="#153-max-capacity-problem" title="Permanent link">&para;</a></h1>
<div class="admonition question">
<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 equals the product of height and 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 such that the capacity of the formed container is maximized, and return the maximum capacity. An example is shown in Figure 15-7.</p>
<p>Given an array <span class="arithmatex">\(ht\)</span>, where each element represents the height of a vertical partition. Any two partitions in the array, together with the space between them, can form a container.</p>
<p>The capacity of the container equals the product of its height and width (that is, its area), where the height is determined by the shorter partition and the width is the difference between the array indices of the two partitions.</p>
<p>Select two partitions in the array such that the capacity of the resulting container is maximized, and return that maximum capacity. An example is shown in Figure 15-7.</p>
</div>
<p><img alt="Example data for the max capacity problem" class="animation-figure" src="../max_capacity_problem.assets/max_capacity_example.png" /></p>
<p align="center"> Figure 15-7 &nbsp; Example data for the max capacity problem </p>
<p>The container is formed by any two partitions, <strong>therefore the state of this problem is the indices of two partitions, denoted as <span class="arithmatex">\([i, j]\)</span></strong>.</p>
<p>According to the problem description, capacity equals height multiplied by width, where height is determined by the shorter partition, and width is the difference in array indices between the two partitions. Let the capacity be <span class="arithmatex">\(cap[i, j]\)</span>, then the calculation formula is:</p>
<p>The container is formed by any two partitions, <strong>so the state of this problem is the indices of the two partitions, denoted by <span class="arithmatex">\([i, j]\)</span></strong>.</p>
<p>According to the problem statement, capacity equals height multiplied by width, where the height is determined by the shorter partition and the width is the difference between the array indices of the two partitions. Let the capacity be <span class="arithmatex">\(cap[i, j]\)</span>; then we obtain the following formula:</p>
<div class="arithmatex">\[
cap[i, j] = \min(ht[i], ht[j]) \times (j - i)
\]</div>
<p>Let the array length be <span class="arithmatex">\(n\)</span>, then the number of combinations of two partitions (total number of states) is <span class="arithmatex">\(C_n^2 = \frac{n(n - 1)}{2}\)</span>. Most directly, <strong>we can exhaustively enumerate all states</strong> to find the maximum capacity, with time complexity <span class="arithmatex">\(O(n^2)\)</span>.</p>
<p>Let the array length be <span class="arithmatex">\(n\)</span>. Then the number of ways to choose two partitions (that is, the total number of states) is <span class="arithmatex">\(C_n^2 = \frac{n(n - 1)}{2}\)</span>. The most straightforward approach is to <strong>exhaustively enumerate all states</strong> to find the maximum capacity, which has a time complexity of <span class="arithmatex">\(O(n^2)\)</span>.</p>
<h3 id="1-greedy-strategy-determination">1. &nbsp; Greedy Strategy Determination<a class="headerlink" href="#1-greedy-strategy-determination" title="Permanent link">&para;</a></h3>
<p>This problem has a more efficient solution. As shown in Figure 15-8, select a state <span class="arithmatex">\([i, j]\)</span> where index <span class="arithmatex">\(i &lt; j\)</span> and height <span class="arithmatex">\(ht[i] &lt; ht[j]\)</span>, meaning <span class="arithmatex">\(i\)</span> is the short partition and <span class="arithmatex">\(j\)</span> is the long partition.</p>
<p>This problem has a more efficient solution. As shown in Figure 15-8, consider a state <span class="arithmatex">\([i, j]\)</span> where <span class="arithmatex">\(i &lt; j\)</span> and <span class="arithmatex">\(ht[i] &lt; ht[j]\)</span>. In this case, <span class="arithmatex">\(i\)</span> is the shorter partition and <span class="arithmatex">\(j\)</span> is the taller partition.</p>
<p><img alt="Initial state" class="animation-figure" src="../max_capacity_problem.assets/max_capacity_initial_state.png" /></p>
<p align="center"> Figure 15-8 &nbsp; Initial state </p>
<p>As shown in Figure 15-9, <strong>if we now move the long partition <span class="arithmatex">\(j\)</span> closer to the short partition <span class="arithmatex">\(i\)</span>, the capacity will definitely decrease</strong>.</p>
<p>This is because after moving the long partition <span class="arithmatex">\(j\)</span>, the width <span class="arithmatex">\(j-i\)</span> definitely decreases; and since height is determined by the short partition, the height can only remain unchanged (<span class="arithmatex">\(i\)</span> is still the short partition) or decrease (the moved <span class="arithmatex">\(j\)</span> becomes the short partition).</p>
<p>As shown in Figure 15-9, <strong>if we now move the taller partition <span class="arithmatex">\(j\)</span> inward toward the shorter partition <span class="arithmatex">\(i\)</span>, the capacity will definitely decrease</strong>.</p>
<p>This is because after moving the taller partition <span class="arithmatex">\(j\)</span>, the width <span class="arithmatex">\(j-i\)</span> definitely decreases. Since the height is determined by the shorter partition, the height can only stay the same (<span class="arithmatex">\(i\)</span> remains the shorter partition) or decrease (<span class="arithmatex">\(j\)</span> becomes the shorter partition after being moved).</p>
<p><img alt="State after moving the long partition inward" class="animation-figure" src="../max_capacity_problem.assets/max_capacity_moving_long_board.png" /></p>
<p align="center"> Figure 15-9 &nbsp; State after moving the long partition inward </p>
<p>Conversely, <strong>we can only possibly increase capacity by contracting the short partition <span class="arithmatex">\(i\)</span> inward</strong>. Because although width will definitely decrease, <strong>height may increase</strong> (the moved short partition <span class="arithmatex">\(i\)</span> may become taller). For example, in Figure 15-10, the area increases after moving the short partition.</p>
<p>Conversely, <strong>only by moving the shorter partition <span class="arithmatex">\(i\)</span> inward can the capacity possibly increase</strong>. Although the width will definitely decrease, <strong>the height may increase</strong> (the moved partition at <span class="arithmatex">\(i\)</span> may be taller). For example, in Figure 15-10, the area increases after moving the shorter partition.</p>
<p><img alt="State after moving the short partition inward" class="animation-figure" src="../max_capacity_problem.assets/max_capacity_moving_short_board.png" /></p>
<p align="center"> Figure 15-10 &nbsp; State after moving the short partition inward </p>
<p>From this we can derive the greedy strategy for this problem: initialize two pointers at both ends of the container, and in each round contract the pointer corresponding to the short partition inward, until the two pointers meet.</p>
<p>From this, we can derive the greedy strategy for this problem: initialize two pointers at the two ends, and in each round move the pointer corresponding to the shorter partition inward until the two pointers meet.</p>
<p>Figure 15-11 shows the execution process of the greedy strategy.</p>
<ol>
<li>In the initial state, pointers <span class="arithmatex">\(i\)</span> and <span class="arithmatex">\(j\)</span> are at both ends of the array.</li>
<li>Calculate the capacity of the current state <span class="arithmatex">\(cap[i, j]\)</span>, and update the maximum capacity.</li>
<li>Compare the heights of partition <span class="arithmatex">\(i\)</span> and partition <span class="arithmatex">\(j\)</span>, and move the short partition inward by one position.</li>
<li>Loop through steps <code>2.</code> and <code>3.</code> until <span class="arithmatex">\(i\)</span> and <span class="arithmatex">\(j\)</span> meet.</li>
<li>Compare the heights of partitions <span class="arithmatex">\(i\)</span> and <span class="arithmatex">\(j\)</span>, and move the pointer corresponding to the shorter partition inward by one position.</li>
<li>Repeat steps <code>2.</code> and <code>3.</code> until <span class="arithmatex">\(i\)</span> and <span class="arithmatex">\(j\)</span> meet.</li>
</ol>
<div class="tabbed-set tabbed-alternate" data-tabs="1:9"><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" /><div class="tabbed-labels"><label for="__tabbed_1_1">&lt;1&gt;</label><label for="__tabbed_1_2">&lt;2&gt;</label><label for="__tabbed_1_3">&lt;3&gt;</label><label for="__tabbed_1_4">&lt;4&gt;</label><label for="__tabbed_1_5">&lt;5&gt;</label><label for="__tabbed_1_6">&lt;6&gt;</label><label for="__tabbed_1_7">&lt;7&gt;</label><label for="__tabbed_1_8">&lt;8&gt;</label><label for="__tabbed_1_9">&lt;9&gt;</label></div>
<div class="tabbed-content">
@@ -4450,8 +4450,8 @@ cap[i, j] = \min(ht[i], ht[j]) \times (j - i)
<p align="center"> Figure 15-11 &nbsp; Greedy process for the max capacity problem </p>
<h3 id="2-code-implementation">2. &nbsp; Code Implementation<a class="headerlink" href="#2-code-implementation" title="Permanent link">&para;</a></h3>
<p>The code loops at most <span class="arithmatex">\(n\)</span> rounds, <strong>therefore the time complexity is <span class="arithmatex">\(O(n)\)</span></strong>.</p>
<p>Variables <span class="arithmatex">\(i\)</span>, <span class="arithmatex">\(j\)</span>, and <span class="arithmatex">\(res\)</span> use a constant amount of extra space, <strong>therefore the space complexity is <span class="arithmatex">\(O(1)\)</span></strong>.</p>
<p>The code runs for at most <span class="arithmatex">\(n\)</span> rounds, <strong>so the time complexity is <span class="arithmatex">\(O(n)\)</span></strong>.</p>
<p>Variables <span class="arithmatex">\(i\)</span>, <span class="arithmatex">\(j\)</span>, and <span class="arithmatex">\(res\)</span> use only a constant amount of extra space, <strong>so the space complexity is <span class="arithmatex">\(O(1)\)</span></strong>.</p>
<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">
<div class="tabbed-block">
@@ -4761,15 +4761,15 @@ cap[i, j] = \min(ht[i], ht[j]) \times (j - i)
</div>
<h3 id="3-correctness-proof">3. &nbsp; Correctness Proof<a class="headerlink" href="#3-correctness-proof" title="Permanent link">&para;</a></h3>
<p>The reason greedy is faster than exhaustive enumeration is that each round of greedy selection "skips" some states.</p>
<p>For example, in state <span class="arithmatex">\(cap[i, j]\)</span> where <span class="arithmatex">\(i\)</span> is the short partition and <span class="arithmatex">\(j\)</span> is the long partition, if we greedily move the short partition <span class="arithmatex">\(i\)</span> inward by one position, the states shown in Figure 15-12 will be "skipped". <strong>This means that the capacities of these states cannot be verified later</strong>.</p>
<p>For example, in state <span class="arithmatex">\(cap[i, j]\)</span>, suppose <span class="arithmatex">\(i\)</span> is the shorter partition and <span class="arithmatex">\(j\)</span> is the taller partition. If we greedily move the shorter partition <span class="arithmatex">\(i\)</span> inward by one position, the states shown in Figure 15-12 will be "skipped." <strong>This means that their capacities can no longer be checked later</strong>.</p>
<div class="arithmatex">\[
cap[i, i+1], cap[i, i+2], \dots, cap[i, j-2], cap[i, j-1]
\]</div>
<p><img alt="States skipped by moving the short partition" class="animation-figure" src="../max_capacity_problem.assets/max_capacity_skipped_states.png" /></p>
<p align="center"> Figure 15-12 &nbsp; States skipped by moving the short partition </p>
<p>Observing carefully, <strong>these skipped states are actually all the states obtained by moving the long partition <span class="arithmatex">\(j\)</span> inward</strong>. We have already proven that moving the long partition inward will definitely decrease capacity. That is, the skipped states cannot possibly be the optimal solution, <strong>skipping them will not cause us to miss the optimal solution</strong>.</p>
<p>The above analysis shows that the operation of moving the short partition is "safe", and the greedy strategy is effective.</p>
<p>A closer look shows that <strong>these skipped states are exactly the states obtained by moving the taller partition <span class="arithmatex">\(j\)</span> inward</strong>. We have already proven that moving the taller partition inward will definitely decrease the capacity. Therefore, none of the skipped states can be the optimal solution, <strong>so skipping them does not cause us to miss the optimum</strong>.</p>
<p>The above analysis shows that moving the shorter partition is a "safe" operation, and that the greedy strategy is effective.</p>
<!-- Source file information -->
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -39,7 +39,7 @@
<title>15.4 Max Product Cutting Problem - Hello Algo</title>
<title>15.4 Maximum Product Cutting Problem - Hello Algo</title>
@@ -99,7 +99,7 @@
<div data-md-component="skip">
<a href="#154-max-product-cutting-problem" class="md-skip">
<a href="#154-maximum-product-cutting-problem" class="md-skip">
Skip to content
</a>
@@ -154,7 +154,7 @@
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
15.4 &nbsp; Max Product Cutting Problem
15.4 &nbsp; Maximum Product Cutting Problem
</span>
</div>
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -3978,10 +3978,10 @@
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#1-greedy-strategy-determination" class="md-nav__link">
<a href="#1-determining-the-greedy-strategy" class="md-nav__link">
<span class="md-ellipsis">
1. &nbsp; Greedy Strategy Determination
1. &nbsp; Determining the Greedy Strategy
</span>
</a>
@@ -4194,7 +4194,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -4309,10 +4309,10 @@
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#1-greedy-strategy-determination" class="md-nav__link">
<a href="#1-determining-the-greedy-strategy" class="md-nav__link">
<span class="md-ellipsis">
1. &nbsp; Greedy Strategy Determination
1. &nbsp; Determining the Greedy Strategy
</span>
</a>
@@ -4378,10 +4378,10 @@
<!-- Page content -->
<h1 id="154-max-product-cutting-problem">15.4 &nbsp; Max Product Cutting Problem<a class="headerlink" href="#154-max-product-cutting-problem" title="Permanent link">&para;</a></h1>
<h1 id="154-maximum-product-cutting-problem">15.4 &nbsp; Maximum Product Cutting Problem<a class="headerlink" href="#154-maximum-product-cutting-problem" title="Permanent link">&para;</a></h1>
<div class="admonition question">
<p class="admonition-title">Question</p>
<p>Given a positive integer <span class="arithmatex">\(n\)</span>, split it into the sum of at least two positive integers, and find the maximum product of all integers after splitting, as shown in Figure 15-13.</p>
<p>Given a positive integer <span class="arithmatex">\(n\)</span>, split it into the sum of at least two positive integers and find the maximum product of the resulting integers, as shown in Figure 15-13.</p>
</div>
<p><img alt="Problem definition of max product cutting" class="animation-figure" src="../max_product_cutting_problem.assets/max_product_cutting_definition.png" /></p>
<p align="center"> Figure 15-13 &nbsp; Problem definition of max product cutting </p>
@@ -4394,9 +4394,9 @@ n = \sum_{i=1}^{m}n_i
<div class="arithmatex">\[
\max(\prod_{i=1}^{m}n_i)
\]</div>
<p>We need to think about: how large should the splitting count <span class="arithmatex">\(m\)</span> be, and what should each <span class="arithmatex">\(n_i\)</span> be?</p>
<h3 id="1-greedy-strategy-determination">1. &nbsp; Greedy Strategy Determination<a class="headerlink" href="#1-greedy-strategy-determination" title="Permanent link">&para;</a></h3>
<p>Based on experience, the product of two integers is often greater than their sum. Suppose we split out a factor of <span class="arithmatex">\(2\)</span> from <span class="arithmatex">\(n\)</span>, then their product is <span class="arithmatex">\(2(n-2)\)</span>. We compare this product with <span class="arithmatex">\(n\)</span>:</p>
<p>We need to determine how many parts <span class="arithmatex">\(m\)</span> there should be and what each <span class="arithmatex">\(n_i\)</span> should be.</p>
<h3 id="1-determining-the-greedy-strategy">1. &nbsp; Determining the Greedy Strategy<a class="headerlink" href="#1-determining-the-greedy-strategy" title="Permanent link">&para;</a></h3>
<p>As a rule of thumb, the product of two integers is often greater than their sum. Suppose we split off a factor of <span class="arithmatex">\(2\)</span> from <span class="arithmatex">\(n\)</span>; the resulting product is <span class="arithmatex">\(2(n-2)\)</span>. We compare this product with <span class="arithmatex">\(n\)</span>:</p>
<div class="arithmatex">\[
\begin{aligned}
2(n-2) &amp; \geq n \newline
@@ -4405,13 +4405,13 @@ n &amp; \geq 4
\end{aligned}
\]</div>
<p>As shown in Figure 15-14, when <span class="arithmatex">\(n \geq 4\)</span>, splitting out a <span class="arithmatex">\(2\)</span> will increase the product, <strong>which indicates that integers greater than or equal to <span class="arithmatex">\(4\)</span> should all be split</strong>.</p>
<p><strong>Greedy strategy one</strong>: If the splitting scheme includes factors <span class="arithmatex">\(\geq 4\)</span>, then they should continue to be split. The final splitting scheme should only contain factors <span class="arithmatex">\(1\)</span>, <span class="arithmatex">\(2\)</span>, and <span class="arithmatex">\(3\)</span>.</p>
<p><strong>Greedy strategy one</strong>: If the splitting scheme contains a factor <span class="arithmatex">\(\geq 4\)</span>, it should be split further. The final splitting scheme should contain only the factors <span class="arithmatex">\(1\)</span>, <span class="arithmatex">\(2\)</span>, and <span class="arithmatex">\(3\)</span>.</p>
<p><img alt="Splitting causes product to increase" class="animation-figure" src="../max_product_cutting_problem.assets/max_product_cutting_greedy_infer1.png" /></p>
<p align="center"> Figure 15-14 &nbsp; Splitting causes product to increase </p>
<p>Next, consider which factor is optimal. Among the three 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, because <span class="arithmatex">\(1 \times (n-1) &lt; n\)</span> always holds, meaning splitting out <span class="arithmatex">\(1\)</span> will actually decrease the product.</p>
<p>As shown in Figure 15-15, when <span class="arithmatex">\(n = 6\)</span>, we have <span class="arithmatex">\(3 \times 3 &gt; 2 \times 2 \times 2\)</span>. <strong>This means that 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 larger product.</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 larger product.</p>
<p><img alt="Optimal splitting factor" class="animation-figure" src="../max_product_cutting_problem.assets/max_product_cutting_greedy_infer2.png" /></p>
<p align="center"> Figure 15-15 &nbsp; Optimal splitting factor </p>
@@ -4419,11 +4419,11 @@ n &amp; \geq 4
<ol>
<li>Input integer <span class="arithmatex">\(n\)</span>, continuously split out factor <span class="arithmatex">\(3\)</span> until the remainder is <span class="arithmatex">\(0\)</span>, <span class="arithmatex">\(1\)</span>, or <span class="arithmatex">\(2\)</span>.</li>
<li>When the remainder is <span class="arithmatex">\(0\)</span>, it means <span class="arithmatex">\(n\)</span> is a multiple of <span class="arithmatex">\(3\)</span>, so no further action is needed.</li>
<li>When the remainder is <span class="arithmatex">\(2\)</span>, do not continue splitting, keep it.</li>
<li>When the remainder is <span class="arithmatex">\(1\)</span>, since <span class="arithmatex">\(2 \times 2 &gt; 1 \times 3\)</span>, the last <span class="arithmatex">\(3\)</span> should be replaced with <span class="arithmatex">\(2\)</span>.</li>
<li>When the remainder is <span class="arithmatex">\(2\)</span>, do not split it further; keep it as is.</li>
<li>When the remainder is <span class="arithmatex">\(1\)</span>, since <span class="arithmatex">\(2 \times 2 &gt; 1 \times 3\)</span>, replace the final <span class="arithmatex">\(3\)</span> and the remaining <span class="arithmatex">\(1\)</span> with two <span class="arithmatex">\(2\)</span>s.</li>
</ol>
<h3 id="2-code-implementation">2. &nbsp; Code Implementation<a class="headerlink" href="#2-code-implementation" title="Permanent link">&para;</a></h3>
<p>As shown in Figure 15-16, we don't need to use loops to split the integer, but can use integer division to get the count of <span class="arithmatex">\(3\)</span>s as <span class="arithmatex">\(a\)</span>, and modulo operation to get the remainder as <span class="arithmatex">\(b\)</span>, at which point we have:</p>
<p>As shown in Figure 15-16, we do not need loops to split the integer. Instead, we use integer division to obtain the number of <span class="arithmatex">\(3\)</span>s, denoted by <span class="arithmatex">\(a\)</span>, and the modulo operation to obtain the remainder <span class="arithmatex">\(b\)</span>, giving:</p>
<div class="arithmatex">\[
n = 3 a + b
\]</div>
@@ -4722,18 +4722,18 @@ n = 3 a + b
<p><img alt="Calculation method for max product cutting" class="animation-figure" src="../max_product_cutting_problem.assets/max_product_cutting_greedy_calculation.png" /></p>
<p align="center"> Figure 15-16 &nbsp; Calculation method for max product cutting </p>
<p><strong>The time complexity depends on the implementation of the exponentiation operation in the programming language</strong>. Taking Python as an example, there are three commonly used power calculation functions.</p>
<p><strong>The time complexity depends on how exponentiation is implemented in the programming language</strong>. Taking Python as an example, there are three commonly used ways to compute powers.</p>
<ul>
<li>Both the operator <code>**</code> and the function <code>pow()</code> have time complexity <span class="arithmatex">\(O(\log a)\)</span>.</li>
<li>The function <code>math.pow()</code> internally calls the C library's <code>pow()</code> function, which performs floating-point exponentiation, with time complexity <span class="arithmatex">\(O(1)\)</span>.</li>
</ul>
<p>Variables <span class="arithmatex">\(a\)</span> and <span class="arithmatex">\(b\)</span> use a constant amount of extra space, <strong>therefore the space complexity is <span class="arithmatex">\(O(1)\)</span></strong>.</p>
<h3 id="3-correctness-proof">3. &nbsp; Correctness Proof<a class="headerlink" href="#3-correctness-proof" title="Permanent link">&para;</a></h3>
<p>Using proof by contradiction, only analyzing the case where <span class="arithmatex">\(n \geq 4\)</span>.</p>
<p>We use proof by contradiction and consider only the case where <span class="arithmatex">\(n \geq 4\)</span>.</p>
<ol>
<li><strong>All factors <span class="arithmatex">\(\leq 3\)</span></strong>: Suppose the optimal splitting scheme includes a factor <span class="arithmatex">\(x \geq 4\)</span>, then it can definitely continue to be split into <span class="arithmatex">\(2(x-2)\)</span> to obtain a larger (or equal) product. This contradicts the assumption.</li>
<li><strong>The splitting scheme does not contain <span class="arithmatex">\(1\)</span></strong>: Suppose the optimal splitting scheme includes a factor of <span class="arithmatex">\(1\)</span>, then it can definitely be merged into another factor to obtain a larger product. This contradicts the assumption.</li>
<li><strong>The splitting scheme contains at most two <span class="arithmatex">\(2\)</span>s</strong>: Suppose the optimal splitting scheme includes three <span class="arithmatex">\(2\)</span>s, then they can definitely be replaced by two <span class="arithmatex">\(3\)</span>s for a larger product. This contradicts the assumption.</li>
<li><strong>All factors <span class="arithmatex">\(\leq 3\)</span></strong>: Suppose the optimal splitting scheme includes a factor <span class="arithmatex">\(x \geq 4\)</span>. Then it can be further split into <span class="arithmatex">\(2(x-2)\)</span> to obtain a larger (or equal) product. This contradicts the assumption.</li>
<li><strong>The splitting scheme does not contain <span class="arithmatex">\(1\)</span></strong>: Suppose the optimal splitting scheme includes a factor of <span class="arithmatex">\(1\)</span>. Then it can be merged into another factor to obtain a larger product. This contradicts the assumption.</li>
<li><strong>The splitting scheme contains at most two <span class="arithmatex">\(2\)</span>s</strong>: Suppose the optimal splitting scheme includes three <span class="arithmatex">\(2\)</span>s. Then they can be replaced by two <span class="arithmatex">\(3\)</span>s, yielding a larger product. This contradicts the assumption.</li>
</ol>
<!-- Source file information -->
+23 -23
View File
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Data Structures and Algorithms Crash Course with Animated Illustrations and Off-the-Shelf Code">
<meta name="description" content="Data structures and algorithms tutorial with animated illustrations and ready-to-run code">
<meta name="author" content="krahets">
@@ -576,7 +576,7 @@
<span class="md-ellipsis">
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
@@ -598,7 +598,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 1. Encounter With Algorithms
Chapter 1. Encounter with Algorithms
</label>
@@ -1183,7 +1183,7 @@
<span class="md-ellipsis">
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
@@ -1205,7 +1205,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 4. Array and Linked List
Chapter 4. Arrays and Linked Lists
</label>
@@ -1311,7 +1311,7 @@
<span class="md-ellipsis">
4.4 Memory and Cache *
4.4 Random-Access Memory and Cache *
@@ -1402,7 +1402,7 @@
<span class="md-ellipsis">
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
@@ -1424,7 +1424,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 5. Stack and Queue
Chapter 5. Stacks and Queues
</label>
@@ -1502,7 +1502,7 @@
<span class="md-ellipsis">
5.3 Double-Ended Queue
5.3 Deque
@@ -1593,7 +1593,7 @@
<span class="md-ellipsis">
Chapter 6. Hashing
Chapter 6. Hash Table
@@ -1615,7 +1615,7 @@
<span class="md-nav__icon md-icon"></span>
Chapter 6. Hashing
Chapter 6. Hash Table
</label>
@@ -1888,7 +1888,7 @@
<span class="md-ellipsis">
7.3 Array Representation of Tree
7.3 Array Representation of Binary Trees
@@ -2107,7 +2107,7 @@
<span class="md-ellipsis">
8.2 Building a Heap
8.2 Heap Construction Operation
@@ -2135,7 +2135,7 @@
<span class="md-ellipsis">
8.3 Top-K Problem
8.3 Top-k Problem
@@ -2493,7 +2493,7 @@
<span class="md-ellipsis">
10.2 Binary Search Insertion
10.2 Binary Search Insertion Point
@@ -2521,7 +2521,7 @@
<span class="md-ellipsis">
10.3 Binary Search Edge Cases
10.3 Binary Search Boundaries
@@ -2577,7 +2577,7 @@
<span class="md-ellipsis">
10.5 Search Algorithms Revisited
10.5 Searching Algorithms Revisited
@@ -2726,7 +2726,7 @@
<span class="md-ellipsis">
11.1 Sorting Algorithms
11.1 Sorting Algorithm
@@ -3199,7 +3199,7 @@
<span class="md-ellipsis">
12.4 Hanoi Tower Problem
12.4 Hanota Problem
@@ -4172,7 +4172,7 @@
<span class="md-ellipsis">
16.3 Terminology Table
16.3 Glossary
@@ -4343,9 +4343,9 @@
<li>In the coin change problem, for certain coin combinations, greedy algorithms can guarantee finding the optimal solution; for other coin combinations, however, greedy algorithms may find very poor solutions.</li>
<li>Problems suitable for solving with greedy algorithms have two major properties: greedy choice property and optimal substructure. The greedy choice property represents the effectiveness of the greedy strategy.</li>
<li>For some complex problems, proving the greedy choice property is not simple. Relatively speaking, disproving it is easier, such as in the coin change problem.</li>
<li>Solving greedy problems mainly consists of three steps: problem analysis, determining the greedy strategy, and correctness proof. Among these, determining the greedy strategy is the core step, and correctness proof is often the difficult point.</li>
<li>The fractional knapsack problem, based on the 0-1 knapsack problem, allows selecting a portion of items, and therefore can be solved using greedy algorithms. The correctness of the greedy strategy can be proven using proof by contradiction.</li>
<li>The max capacity problem can be solved using exhaustive enumeration with time complexity <span class="arithmatex">\(O(n^2)\)</span>. By designing a greedy strategy to move the short partition inward in each round, the time complexity can be optimized to <span class="arithmatex">\(O(n)\)</span>.</li>
<li>Solving greedy problems mainly consists of three steps: problem analysis, determining the greedy strategy, and correctness proof. Among these, determining the greedy strategy is the core step, and correctness proof is often the main difficulty.</li>
<li>The fractional knapsack problem, based on the 0-1 knapsack problem, allows selecting fractions of items, and therefore can be solved using greedy algorithms. The correctness of the greedy strategy can be proven using proof by contradiction.</li>
<li>The max capacity problem can be solved using exhaustive enumeration with time complexity <span class="arithmatex">\(O(n^2)\)</span>. By designing a greedy strategy to move the shorter side inward in each round, the time complexity can be optimized to <span class="arithmatex">\(O(n)\)</span>.</li>
<li>In the max product cutting problem, we successively derive two greedy strategies: integers <span class="arithmatex">\(\geq 4\)</span> should all continue to be split, and the optimal splitting factor is <span class="arithmatex">\(3\)</span>. The code includes exponentiation operations, and the time complexity depends on the implementation method of exponentiation, typically being <span class="arithmatex">\(O(1)\)</span> or <span class="arithmatex">\(O(\log n)\)</span>.</li>
</ul>