Greedy¶
In one sentence
A greedy algorithm makes the choice that looks best right now and never takes it back. It is only correct when you can argue that some best answer agrees with that choice, so every greedy needs a short proof.
Where this comes from
Greedy was our seventh problem set, 7-Greedy, assigned on 10 May and worked through in May. There is no separate lesson note, so this page is written from scratch around that set and the greedy proofs we met in AIO.
1. What problem does it solve?¶
Example — 2193B · Reverse a Permutation (7-Greedy · D, rating 800)
You are given a permutation \(p\) of \(1 \dots n\). Choose exactly one segment \([l, r]\) and reverse it. Output the lexicographically largest permutation you can get.
Limits: \(t \le 10^4\), \(n \le 2\cdot10^5\), sum of \(n \le 2\cdot10^5\).
Input Output
4
4
3 2 1 4 4 1 2 3
3
3 1 2 3 2 1
4
4 3 2 1 4 3 2 1
2
2 1 2 1
Lexicographic order is decided by the first difference. So we should make position \(1\) as large as possible, then position \(2\), and so on. Trying all \(O(n^2)\) segments and comparing \(O(n)\) arrays is \(O(n^3)\); the greedy idea gives \(O(n)\).
2. The math¶
2.1 The greedy choice¶
The best possible array is \(n, n-1, \dots, 1\). Let \(i\) be the first position where \(p_i \ne n - i + 1\) (1-indexed).
- If there is no such \(i\), \(p\) is already the best; reverse a single element (\(l = r\)) and print \(p\).
- Otherwise, the value \(v = n - i + 1\) sits at some position \(j > i\) (it cannot be earlier, because positions \(1 \dots i-1\) hold \(n, \dots, v+1\)). Reverse \([i, j]\): this brings \(v\) to position \(i\).
2.2 Why it is optimal¶
Compare our result with any other reversal \([l, r]\).
- Positions before \(i\) cannot improve. They already hold the largest possible values.
- Starting before \(i\) only hurts. If \(l < i\), position \(l\) receives \(p_r\). All values bigger than or equal to \(p_l = n - l + 1\) are at positions \(\le l\), so \(p_r < p_l\) unless \(r = l\). The array gets smaller at position \(l\).
- Position \(i\) can be at most \(v\). Every bigger value is already used before \(i\). Our reversal achieves \(v\); a reversal with \(l > i\) leaves \(p_i < v\) in place, and a reversal with \(l = i\) puts \(p_r\) at position \(i\), which equals \(v\) only for \(r = j\).
So every other choice is worse at the first position where it differs, or it is our reversal. This "it agrees with us, or we can show it is worse" pattern is how most greedy proofs go.
2.3 Trace on sample 1¶
\(n = 4\), \(p = (3, 2, 1, 4)\). Target \((4, 3, 2, 1)\). First mismatch: \(i = 1\) (\(3 \ne 4\)). Value \(4\) is at \(j = 4\). Reverse \([1, 4]\) → \((4, 1, 2, 3)\).
Sample 2: \(p = (3, 1, 2)\), target \((3, 2, 1)\). Position \(1\) matches; \(i = 2\), value \(2\) is at \(j = 3\); reverse \([2, 3]\) → \((3, 2, 1)\).
2.4 Three ways to prove a greedy¶
| Method | Idea | Where we used it |
|---|---|---|
| first difference | fix the answer position by position; anything else is worse where it first differs | this page |
| exchange argument | take any optimal answer that disagrees with greedy; swap two things to make it agree without getting worse | AIO 2024 · Shopping Spree, AIO 2019 · RPS, AIO 2025 · ORAC |
| stays ahead | after every step, greedy has at least as much room / as good a state as any other plan | AIO 2022 · TSP, AIO 2021 · Social Distancing |
3. Template¶
There is no single greedy template. The shape that covers most problems in the set is:
1. Decide what "best right now" means (smallest end, largest value, closest to the goal, ...).
2. Usually sort by that key.
3. Scan once, taking the best choice that is still allowed.
4. Before coding: write the 2-3 line proof (§2.4). If you cannot, try a tiny counterexample by hand.
4. Worked solution — Reverse a Permutation¶
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
int n;
cin >> n;
vector<int> p(n);
for (int i = 0; i < n; i++) {
cin >> p[i];
}
// first position (0-indexed) that does not hold n - i
int i = 0;
while (i < n && p[i] == n - i) {
i++;
}
if (i < n) {
int j = i;
while (p[j] != n - i) {
j++;
}
reverse(p.begin() + i, p.begin() + j + 1);
}
for (int k = 0; k < n; k++) {
cout << p[k] << " ";
}
cout << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int T = 1;
cin >> T;
for (int i = 0; i < T; i++) {
solve();
}
return 0;
}
Both while loops together move forward at most \(n\) steps, and reverse is \(O(n)\), so each test is \(O(n)\).
5. Common mistakes¶
A greedy that \"obviously\" works
Many greedies are wrong. Before submitting, test your rule on 3–4 tiny inputs where you can find the true answer by hand, and look for a case where a worse-looking first step pays off later.
Sorting by the wrong key
"Earliest start" and "earliest end" lead to different algorithms for interval problems; only one is usually right. Say out loud why your key is the right one.
Proving the easy half only
Showing that greedy gives a valid answer is not enough; the proof must show that no other answer is better.
6. Practice¶
| Problem | Set | Rating | Greedy idea |
|---|---|---|---|
| 2200A · Eating Game | 7-Greedy · A | 800 | only players with the most dishes can eat last |
| 2218B · The 67th 6-7 Integer Problem | 7-Greedy · B | 800 | — |
| 2194A · Lawn Mower | 7-Greedy · C | 800 | keep one board in every block of \(w\): \(n - \lfloor n/w \rfloor\) |
| 2193B · Reverse a Permutation | 7-Greedy · D | 800 | first difference (this page) |
| 2193C · Replace and Sum | 7-Greedy · E | 1000 | — |
| 2190A · Sorting Game | 7-Greedy · F | 1200 | — |
| 2189A · Table with Numbers | 7-Greedy · G | 800 | — |
| 2189B · The Curse of the Frog | 7-Greedy · H | 1200 | — |
| 2188B · Seats | 7-Greedy · I | 1000 | — |
| 2185B · Prefix Max | 7-Greedy · J | 800 | — |
| AIO 2022 · TSP | AIO 2022 Q3 | — | sell as little as allowed each day |
| AIO 2024 · Backpacking | AIO 2024 Q4 | — | buy just enough to reach the next cheaper town |
Credits & licenses
Everything on this page (example proof, method table, worked solution, mistakes, practice notes) is ours. Problem statements are summarised; the originals are on Codeforces.