|
1 | | -## Greedy Algorithms |
| 1 | +## Greedy algorithms |
2 | 2 |
|
3 | | -Greedy methods construct a solution piece by piece, always choosing the currently best-looking option according to a simple rule. The subtlety is not the rule itself but the proof that local optimality extends to global optimality. Two proof tools do most of the work: exchange arguments (you can swap an optimal solution’s first “deviation” back to the greedy choice without harm) and loop invariants (you maintain a statement that pins down exactly what your partial solution guarantees at each step). |
4 | | - |
5 | | -Formally, consider a finite ground set $E$, a family of feasible subsets $\mathcal{F}\subseteq 2^E$, and a weight function $w:E\to \mathbb{R}$. A generic greedy scheme orders elements of $E$ by a key $\kappa(e)$ and scans them, adding $e$ to the building solution $S$ if $S\cup\{e\}\in\mathcal{F}$. Correctness means |
6 | | - |
7 | | -$$ |
8 | | -\text{Greedy}(E,\mathcal{F},w,\kappa)\in\arg\max\{\,w(S):S\in\mathcal{F}\,\}. |
9 | | -$$ |
10 | | - |
11 | | -The nice, crisp setting where this always works is the theory of matroids. Outside that, correctness must be argued problem-by-problem. |
| 3 | +Greedy algorithms build a solution one step at a time. At each step, grab the option that looks best *right now* by some simple rule (highest value, earliest finish, shortest length, etc.). Keep it if it doesn’t break the rules of the problem. |
12 | 4 |
|
13 | 5 | ``` |
14 | | -scan order: e1 e2 e3 e4 e5 ... |
15 | | -feasible? Y N Y Y N |
16 | | -solution S: {e1, e3, e4} |
| 6 | +1) Sort by your rule (the “key”). |
| 7 | +2) Scan items in that order. |
| 8 | +3) If adding this item keeps the partial answer valid, keep it. |
| 9 | +4) Otherwise skip it. |
17 | 10 | ``` |
18 | 11 |
|
19 | | -### The greedy-choice principle and exchange arguments |
20 | | - |
21 | | -Greedy methods feel simple on the surface—always take the best-looking move right now—but the proof that this is globally safe is subtle. The core idea is to show that at the first moment an optimal solution “disagrees” with your greedy choice, you can surgically swap in the greedy move without making things worse. Do that repeatedly and you literally transform some optimal solution into the greedy one. That’s the exchange argument. |
22 | | - |
23 | | -Let $E$ be a finite ground set of “atoms.” Feasible solutions are subsets $S\subseteq E$ belonging to a family $\mathcal{F}\subseteq 2^E$. The objective is additive: |
24 | | - |
25 | | -$$ |
26 | | -\text{maximize } w(S)=\sum_{e\in S} w(e)\quad\text{subject to } S\in\mathcal{F},\qquad w:E\to\mathbb{R}. |
27 | | -$$ |
| 12 | +Picking the best “now” doesn’t obviously give the best “overall.” The real work is showing that these local choices still lead to a globally best answer. |
28 | 13 |
|
29 | | -A generic greedy algorithm fixes an order $e_1,e_2,\dots,e_m$ determined by a key $\kappa$ (for example, sort by nonincreasing $w$ or by earliest finishing time), then scans the elements and keeps $e_i$ whenever $S\cup\{e_i\}\in\mathcal{F}$. |
| 14 | +**Two proof tricks you’ll see a lot:** |
30 | 15 |
|
31 | | -Two structural properties make the exchange proof go through. |
| 16 | +* *Exchange argument.* Take any optimal solution that disagrees with greedy at the first point. Show you can “swap in” the greedy choice there without making the solution worse or breaking feasibility. Do this repeatedly and you morph some optimal solution into the greedy one—so greedy must be optimal. |
| 17 | +* *Loop invariant.* Write down a sentence that’s true after every step of the scan (e.g., “the current set is feasible and as good as any other set built from the items we’ve seen”). Prove it stays true as you process the next item; at the end, that sentence implies optimality. |
32 | 18 |
|
33 | | -1. Feasibility exchange. Whenever $A,B\in\mathcal{F}$ with $|A|<|B|$, there exists $x\in B\setminus A$ such that $A\cup\{x\}\in\mathcal{F}$. This “augmentation flavor” is what lets you replace a non-greedy element by a greedy one while staying feasible. |
| 19 | +*Picture it like this:* |
34 | 20 |
|
35 | | -2. Local dominance. At the first position where greedy would keep $g$ but some optimal $O$ keeps $o\neq g$, you can drop some element $x\in O\setminus A$ and insert $g$ so that |
| 21 | +``` |
| 22 | +position → 1 2 3 4 5 |
| 23 | +greedy: [✓] [✗] [✓] [✓] [✗] |
| 24 | +some optimal: |
| 25 | + ✓ ✓ ✗ ? ? |
| 26 | +First mismatch at 3 → swap in greedy’s pick without harm. |
| 27 | +Repeat until both rows match → greedy is optimal. |
| 28 | +``` |
36 | 29 |
|
37 | | -$$ |
38 | | -A\cup\{g\}\cup\bigl(O\setminus\{x\}\bigr)\in\mathcal{F} |
39 | | -\quad\text{and}\quad |
40 | | -w(g)\ge w(x), |
41 | | -$$ |
| 30 | +**Where greedy shines automatically: matroids (nice constraint systems).** |
| 31 | +There’s a tidy setting where greedy is *always* right (for nonnegative weights): when your “what’s allowed” rules form a **matroid**. You don’t need the symbols—just the vibe: |
42 | 32 |
|
43 | | -where $A$ is the common prefix chosen by both up to that point. The inequality ensures the objective does not decrease during the swap. |
| 33 | +1. **You can start from empty.** |
| 34 | +2. **Throwing things out never hurts.** If a set is allowed, any subset is allowed. |
| 35 | +3. **Smooth growth (augmentation).** If one allowed set is smaller than another, you can always add *something* from the bigger one to the smaller and stay allowed. |
44 | 36 |
|
45 | | -When $(E,\mathcal{F})$ is a matroid, the feasibility exchange always holds; if you also order by nonincreasing $w$, local dominance holds trivially with $x$ chosen by the matroid’s augmentation. Many everyday problems satisfy these two properties even without full matroid machinery. |
| 37 | +That third rule prevents dead ends and is exactly what exchange arguments rely on. In matroids, the simple “sort by weight and take what fits” greedy is guaranteed optimal. Outside matroids, greedy can still work—but you must justify it for the specific problem using exchange/invariants. |
46 | 38 |
|
47 | | -Write the greedy picks as a sequence $G=(g_1,g_2,\dots,g_k)$, in the order chosen. The following lemma is the workhorse. |
48 | 39 |
|
49 | | -**Lemma (first-difference exchange).** Suppose there exists an optimal solution $O$ whose first $t-1$ elements agree with greedy, meaning $g_1,\dots,g_{t-1}\in O$. If $g_t\in O$ as well, continue. Otherwise there exists $x\in O\setminus\{g_1,\dots,g_{t-1}\}$ such that |
| 40 | +### Reachability on a line |
50 | 41 |
|
51 | | -$$ |
52 | | -O' \;=\;\bigl(O\setminus\{x\}\bigr)\cup\{g_t\}\in\mathcal{F} |
53 | | -\quad\text{and}\quad |
54 | | -w(O')\ge w(O). |
55 | | -$$ |
| 42 | +- You stand at square \$0\$ on squares \$0,1,\dots,n-1\$. |
| 43 | +- Each square \$i\$ has a jump power \$a\[i]\$. From \$i\$ you may land on any of \$i+1, i+2, \dots, i+a\[i]\$. |
| 44 | +- Goal: decide if you can reach \$n-1\$; if not, report the furthest reachable square. |
56 | 45 |
|
57 | | -Hence there is an optimal solution that agrees with greedy on the first $t$ positions. |
| 46 | +Example |
58 | 47 |
|
59 | | -*Proof sketch.* Let $A_{t-1}=\{g_1,\dots,g_{t-1}\}$. Because greedy considered $g_t$ before any element in $O\setminus A_{t-1}$ that it skipped, local dominance says some $x\in O\setminus A_{t-1}$ can be traded for $g_t$ without breaking feasibility and without decreasing weight. This creates $O'$ optimal and consistent with greedy for one more step. Apply the same reasoning inductively. |
| 48 | +* Input: \$a=\[3,1,0,0,4,1]\$, so \$n=6\$ (squares \$0..5\$). |
60 | 49 |
|
61 | | -Induction on $t$ yields the main theorem: there exists an optimal solution that agrees with greedy everywhere, hence greedy is optimal. |
| 50 | +``` |
| 51 | +indices: 0 1 2 3 4 5 |
| 52 | +a[i] : 3 1 0 0 4 1 |
| 53 | +reach : ^ start at 0 |
| 54 | +``` |
62 | 55 |
|
63 | | -It helps to picture the two solutions aligned in the greedy order. The top row is the greedy decision at each position; the bottom row is some optimal solution, possibly disagreeing. At the first disagreement, one swap pushes the optimal line upward to match greedy, and the objective value does not drop. |
| 56 | +From any \$i\$, the allowed landings are a range: |
64 | 57 |
|
65 | 58 | ``` |
66 | | -positions → 1 2 3 4 5 6 7 |
67 | | -greedy G: [g1] [g2] [g3] [g4] [g5] [g6] [g7] |
68 | | -optimal O: [g1] [g2] [ o ] [ ? ] [ ? ] [ ? ] [ ? ] |
69 | | - |
70 | | -exchange at position 3: |
71 | | -drop some x from O beyond position 2 and insert g3 |
72 | | -
|
73 | | -after swap: |
74 | | -optimal O': [g1] [g2] [g3] [ ? ] [ ? ] [ ? ] [ ? ] |
| 59 | +i=0 (a[0]=3): 1..3 |
| 60 | +i=1 (a[1]=1): 2 |
| 61 | +i=2 (a[2]=0): — |
| 62 | +i=3 (a[3]=0): — |
| 63 | +i=4 (a[4]=4): 5..8 (board ends at 5) |
75 | 64 | ``` |
76 | 65 |
|
77 | | -The key is not the letter symbols but the invariants. Up to position $t-1$, both solutions coincide. The swap keeps feasibility and weight, so you have a new optimal that also matches at position $t$. Repeat, and the bottom row becomes the top row. |
| 66 | +--- |
78 | 67 |
|
79 | | -### Matroids |
| 68 | +Baseline idea (waves) |
80 | 69 |
|
81 | | -Greedy methods don’t usually get ironclad guarantees, but there is a beautiful class of feasibility systems where they do. That class is the matroids. Once your constraints form a matroid, the simplest weight-ordered greedy scan is not a heuristic anymore; it is provably optimal for every nonnegative weight assignment. |
| 70 | +“Paint everything reachable, one wave at a time.” |
82 | 71 |
|
83 | | -A matroid is a pair $(E,\mathcal{I})$ with $E$ a finite ground set and $\mathcal{I}\subseteq 2^E$ the “independent” subsets. Three axioms hold. |
| 72 | +1. Start with \${0}\$ reachable. |
| 73 | +2. For each already-reachable \$i\$, add all \$i+1..i+a\[i]\$. |
| 74 | +3. Stop when nothing new appears. |
84 | 75 |
|
85 | | -* Non-emptiness says $\varnothing\in\mathcal{I}$. |
86 | | -* Heredity says independence is downward-closed: if $A\in\mathcal{I}$ and $B\subseteq A$, then $B\in\mathcal{I}$. |
87 | | -* Augmentation says independence grows smoothly: if $A,B\in\mathcal{I}$ with $|A|<|B|$, then some $x\in B\setminus A$ exists with $A\cup\{x\}\in\mathcal{I}$. |
| 76 | +Walk on the example: |
88 | 77 |
|
89 | | -The last axiom is the heart. It forbids “dead ends” where a smaller feasible set cannot absorb a single element from any larger feasible set. That smoothness is exactly what greedy needs to keep repairing early choices. |
| 78 | +``` |
| 79 | +start: reachable = {0} |
| 80 | +from 0: add {1,2,3} → reachable = {0,1,2,3} |
| 81 | +from 1: add {2} → no change |
| 82 | +from 2: add {} → a[2]=0 |
| 83 | +from 3: add {} → a[3]=0 |
| 84 | +stop: no new squares → furthest = 3; last (5) unreachable |
| 85 | +``` |
90 | 86 |
|
91 | | -### Reachability on a line |
| 87 | +Correct, but can reprocess many squares. |
92 | 88 |
|
93 | | -You’re standing on square 0 of a line of squares $0,1,\dots,n-1$. |
94 | | -Each square $i$ tells you how far you’re allowed to jump forward from there: a number $a[i]$. From $i$, you can jump to any square $i+1, i+2, \dots, i+a[i]$. The goal is to decide whether you can ever reach the last square, and, if not, what the furthest square is that you can reach. |
| 89 | +--- |
95 | 90 |
|
96 | | -**Example inputs and outputs** |
| 91 | +One-pass trick (frontier) |
97 | 92 |
|
98 | | -Input array: `a = [3, 1, 0, 0, 4, 1]` |
99 | | -There are 6 squares (0 through 5). |
100 | | -Correct output: you cannot reach the last square; the furthest you can get is square `3`. |
| 93 | +Carry one number while scanning left→right: the furthest frontier \$F\$ seen so far. |
101 | 94 |
|
102 | | -Baseline (slow) |
| 95 | +Rules: |
103 | 96 |
|
104 | | -Think “paint everything I can reach, one wave at a time.” |
| 97 | +* If you are at \$i\$ with \$i>F\$, you hit a gap → stuck forever. |
| 98 | +* Otherwise, extend \$F \leftarrow \max(F,\ i+a\[i])\$ and continue. |
105 | 99 |
|
106 | | -1. Start with square 0 marked “reachable.” |
107 | | -2. For every square already marked, paint all squares it can jump to. |
108 | | -3. Keep doing this until no new squares get painted. |
| 100 | +At the end: |
109 | 101 |
|
110 | | -This is correct because you literally try every allowed jump from every spot you know is reachable. It can be wasteful, though, because the same squares get reconsidered over and over in dense cases. |
| 102 | +* Can reach last iff \$F \ge n-1\$. |
| 103 | +* Furthest reachable square is \$F\$ (capped by \$n-1\$). |
111 | 104 |
|
112 | | -Walking the example: |
| 105 | +Pseudocode |
113 | 106 |
|
114 | 107 | ``` |
115 | | -start: reachable = {0} |
116 | | -from 0: can reach {1,2,3} → reachable = {0,1,2,3} |
117 | | -from 1: can reach {2} → no change |
118 | | -from 2: can reach {} → no change (a[2]=0) |
119 | | -from 3: can reach {} → no change (a[3]=0) |
120 | | -done: no new squares → furthest is 3, last is unreachable |
| 108 | +F = 0 |
| 109 | +for i in 0..n-1: |
| 110 | + if i > F: break |
| 111 | + F = max(F, i + a[i]) |
| 112 | +
|
| 113 | +can_reach_last = (F >= n-1) |
| 114 | +furthest = min(F, n-1) |
121 | 115 | ``` |
122 | 116 |
|
123 | | -**How it works** |
| 117 | +Why this is safe (one line): \$F\$ always equals “best jump end discovered from any truly-reachable square \$\le i\$,” and never decreases; if \$i>F\$, no earlier jump can help because its effect was already folded into \$F\$. |
124 | 118 |
|
125 | | -Carry one number as you sweep left to right: `F`, the furthest square you can reach **so far**. |
126 | | -Rule of thumb: |
| 119 | + walkthrough on the example |
127 | 120 |
|
128 | | -* If you’re looking at square `i` and `i` is beyond `F`, you’re stuck forever. |
129 | | -* Otherwise, extend the frontier with `F = max(F, i + a[i])` and move on. |
| 121 | +We draw the frontier as a bracket reaching to \$F\$. |
130 | 122 |
|
131 | | -That’s it—one pass, no backtracking. |
| 123 | +Step \$i=0\$ (inside frontier since \$0\le F\$); update \$F=\max(0,0+3)=3\$. |
| 124 | + |
| 125 | +``` |
| 126 | +indices: 0 1 2 3 4 5 |
| 127 | + [===============F] |
| 128 | + 0 1 2 3 |
| 129 | +F=3 |
| 130 | +``` |
132 | 131 |
|
133 | | -Why this is safe in a sentence: `F` always summarizes “the best jump end we have discovered from any square we truly reached,” and it never goes backward; if you hit a gap where `i > F`, then no earlier jump can help because its effect was already folded into `F`. |
| 132 | +Step \$i=1\$: still \$i\le F\$. Update \$F=\max(3,1+1)=3\$ (no change). |
| 133 | +Step \$i=2\$: \$F=\max(3,2+0)=3\$ (no change). |
| 134 | +Step \$i=3\$: \$F=\max(3,3+0)=3\$ (no change). |
134 | 135 |
|
135 | | -Plugging in the same numbers |
| 136 | +Now \$i=4\$ but \$4>F(=3)\$ → gap → stuck. |
136 | 137 |
|
137 | 138 | ``` |
138 | | -a = [3, 1, 0, 0, 4, 1] |
139 | | -n = 6 |
140 | | -F = 0 # we start at square 0 (we’ll extend immediately at i=0) |
141 | | -
|
142 | | -i=0: 0 ≤ F → F = max(0, 0+3) = 3 |
143 | | -i=1: 1 ≤ F → F = max(3, 1+1) = 3 |
144 | | -i=2: 2 ≤ F → F = max(3, 2+0) = 3 |
145 | | -i=3: 3 ≤ F → F = max(3, 3+0) = 3 |
146 | | -i=4: 4 > F → stuck here |
| 139 | +indices: 0 1 2 3 4 5 |
| 140 | + [===============F] x (i=4 is outside) |
| 141 | +F=3 |
147 | 142 | ``` |
148 | 143 |
|
149 | | -Final state: `F = 3`, which means the furthest reachable square is 3. Since `F < n-1 = 5`, the last square is not reachable. |
| 144 | +Final: \$F=3\$. Since \$F\<n-1=5\$, last is unreachable; furthest reachable square is \$3\$. |
150 | 145 |
|
151 | | -Summary |
| 146 | +Complexity: time \$O(n)\$, space \$O(1)\$. |
152 | 147 |
|
153 | | -* Time: $O(n)$ (single left-to-right pass) |
154 | | -* Space: $O(1)$ |
155 | 148 |
|
156 | 149 | ### Minimum spanning trees |
157 | 150 |
|
|
0 commit comments