Greedy Algorithms
Greedy অ্যালগরিদম
1. The Greedy Idea — Local Choice, Global Hope
A greedy algorithm builds a solution one piece at a time, always making the choice that looks best right now, never reconsidering. When the problem has the right structure, that local rule produces a globally optimal answer — and it is fast: usually O(n log n) or even O(n).
2. The Two Properties That Make Greedy Work
A problem is solvable by greedy if and only if it has both:
- Greedy-choice property — a global optimum can always be obtained by making a locally optimal choice.
- Optimal substructure — the optimal solution to the whole problem contains optimal solutions to its subproblems.
3. Activity Selection — The Classic
Given n activities each with a start and finish time, select the maximum number that can be done by a single person (no two overlap). The right greedy: sort by finish time, then iterate, picking each activity whose start ≥ last picked finish.
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<pair<int,int>> act = {
{0,4}, {1,5}, {4,8}, {6,9},
{8,12}, {10,14}, {12,16}
};
// Sort by finish time (.second)
sort(act.begin(), act.end(), [](auto& a, auto& b){
return a.second < b.second;
});
int count = 0, lastEnd = INT_MIN;
for (auto& [s, e] : act) {
if (s >= lastEnd) {
cout << "Pick [" << s << "," << e << "]\n";
lastEnd = e;
++count;
}
}
cout << "Total selected = " << count << "\n";
}
4. Huffman Coding — Optimal Prefix Codes
Given letter frequencies, build a prefix-free binary code that minimises the total encoded length. Huffman's algorithm repeatedly combines the two least-frequent nodes into a new internal node — using a min-heap, that's O(n log n).
#include <bits/stdc++.h>
using namespace std;
struct Node { int freq; char ch; Node *l=nullptr, *r=nullptr; };
struct Cmp { bool operator()(Node* a, Node* b){ return a->freq > b->freq; } };
void codes(Node* n, string p, map<char,string>& out) {
if (!n) return;
if (!n->l && !n->r) { out[n->ch] = p.empty() ? "0" : p; return; }
codes(n->l, p+"0", out);
codes(n->r, p+"1", out);
}
int main() {
vector<pair<char,int>> freq = {{'a',45},{'b',13},{'c',12},{'d',14},{'e',16}};
priority_queue<Node*, vector<Node*>, Cmp> pq;
for (auto& [c, f] : freq) pq.push(new Node{f, c});
while (pq.size() > 1) {
Node* a = pq.top(); pq.pop();
Node* b = pq.top(); pq.pop();
Node* parent = new Node{a->freq + b->freq, '#', a, b};
pq.push(parent);
}
map<char,string> out;
codes(pq.top(), "", out);
for (auto& [c, code] : out)
cout << c << " -> " << code << "\n";
}
5. Fractional vs 0/1 Knapsack — When Greedy Breaks
Fractional knapsack (you may take any fraction of an item): sort by value/weight ratio descending, take as much of each item as fits — provably optimal. 0/1 knapsack (you must take or leave): the same greedy may be arbitrarily bad. That's a DP problem (Lecture 35).
✅ Fractional Knapsack — Greedy Works
Sort items by value/weight ratio. Take items in order; if next won't fit fully, take a fraction. O(n log n).
⚠️ 0/1 Knapsack — Greedy Fails
Items: (60, 10), (100, 20), (120, 30), W=50. Greedy by ratio picks first two (160). DP picks last two (220).
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<pair<int,int>> items = {{60,10}, {100,20}, {120,30}};
int W = 50;
sort(items.begin(), items.end(), [](auto& a, auto& b){
return (double)a.first/a.second > (double)b.first/b.second;
});
double value = 0;
for (auto& [v, w] : items) {
if (W >= w) { value += v; W -= w; }
else { value += v * ((double)W / w); break; }
}
cout << "Max value = " << value << "\n";
}
6. Famous Greedy Failures
| Problem | Greedy Idea | Verdict |
|---|---|---|
| Coin change with arbitrary denominations | Always pick largest coin ≤ remaining | ❌ Fails for {1,3,4} with target 6: greedy = 4+1+1, optimal = 3+3 |
| 0/1 knapsack | Sort by value/weight | ❌ Counter-example above |
| Travelling Salesman | Always go to nearest unvisited city | ❌ NP-hard; greedy can be 25%+ worse than optimal |
| Longest path in DAG | Pick highest-weight edge each step | ❌ Use DP topologically instead |
7. Practice Problems
-
Jump Game — given
nums[i]= max jump length from i, can you reach the last index?প্রতিটি index থেকে সর্বাধিক jump length দেওয়া। শেষ index-এ পৌঁছানো যাবে কিনা?✨ Show Answer
jump.cpp#include <bits/stdc++.h> using namespace std; int main(){ vector<int> a = {2,3,1,1,4}; int reach = 0; for (int i = 0; i < (int)a.size(); ++i) { if (i > reach) { cout << "NO\n"; return 0; } reach = max(reach, i + a[i]); } cout << "YES\n"; } -
Minimum number of platforms needed at Kamalapur Railway Station given arrival/departure times.Kamalapur Railway Station-এ ট্রেনের arrival/departure সময় দেওয়া। সর্বনিম্ন কতগুলো platform লাগবে?
✨ Show Answer
platforms.cpp#include <bits/stdc++.h> using namespace std; int main(){ vector<int> arr = {900, 940, 950, 1100, 1500, 1800}; vector<int> dep = {910, 1200, 1120, 1130, 1900, 2000}; sort(arr.begin(), arr.end()); sort(dep.begin(), dep.end()); int i = 0, j = 0, cur = 0, ans = 0; while (i < (int)arr.size()) { if (arr[i] <= dep[j]) { ++cur; ++i; ans = max(ans, cur); } else { --cur; ++j; } } cout << "Platforms = " << ans << "\n"; } -
Gas station circuit — given gas[i] and cost[i] to travel to next station, find a starting station to complete the loop.প্রতিটি station-এ gas[i] পাওয়া যায় এবং পরের station-এ যেতে cost[i] লাগে। কোন station থেকে শুরু করলে পুরো বৃত্ত শেষ হবে?
✨ Show Answer
gas.cpp#include <bits/stdc++.h> using namespace std; int main(){ vector<int> gas = {1,2,3,4,5}, cost = {3,4,5,1,2}; int tank=0, total=0, start=0; for (int i=0; i < (int)gas.size(); ++i) { int d = gas[i] - cost[i]; tank += d; total += d; if (tank < 0) { start = i+1; tank = 0; } } cout << (total >= 0 ? start : -1) << "\n"; } -
Assign Cookies — give each child the smallest cookie that satisfies their greed. Maximise satisfied children.প্রতিটি শিশুকে এমন কুকি দিন যা তার greed পূরণ করে। সর্বাধিক কতজন সন্তুষ্ট হবে?
✨ Show Answer
cookies.cpp#include <bits/stdc++.h> using namespace std; int main(){ vector<int> g = {1,2,3}, s = {1,1,2,3}; sort(g.begin(), g.end()); sort(s.begin(), s.end()); int i=0, j=0; while (i<(int)g.size() && j<(int)s.size()) { if (s[j] >= g[i]) ++i; ++j; } cout << i << "\n"; } -
Minimum coins for canonical Bangladesh denominations {1,2,5,10,20,50,100,500} to make amount 678.Bangladesh-এর {১,২,৫,১০,২০,৫০,১০০,৫০০} টাকার কয়েনে ৬৭৮ টাকা সর্বনিম্ন কতগুলো কয়েনে দেওয়া যাবে?
✨ Show Answer
coins.cpp#include <bits/stdc++.h> using namespace std; int main(){ vector<int> d = {500,100,50,20,10,5,2,1}; int amt = 678, cnt = 0; for (int c : d) while (amt >= c) { amt -= c; ++cnt; } cout << "Coins = " << cnt << "\n"; }Output:
Coins = 7(500 + 100 + 50 + 20 + 5 + 2 + 1 = 678). -
Job sequencing with deadlines — each job has profit p[i] and deadline d[i]. Maximise profit, each job takes 1 time unit.প্রতিটি job-এর deadline ও profit আছে। প্রতিটি ১ unit সময় নেয়। সর্বাধিক profit বের করুন।
✨ Show Answer
job_seq.cpp#include <bits/stdc++.h> using namespace std; int main(){ vector<tuple<int,int>> jobs = {{100,2},{19,1},{27,2},{25,1},{15,3}}; sort(jobs.begin(), jobs.end(), greater<>()); vector<bool> slot(10, false); int profit = 0; for (auto& [p, d] : jobs) { for (int t = d; t > 0; --t) if (!slot[t]) { slot[t] = true; profit += p; break; } } cout << "Max profit = " << profit << "\n"; } -
Lemonade change — customers pay 5, 10, or 20 taka for a 5-taka drink. Can you give correct change to everyone?৫ টাকার লেবু-পানি — customer ৫, ১০, বা ২০ টাকা দেয়। সবাইকে সঠিক change দিতে পারবেন?
✨ Show Answer
lemonade.cpp#include <bits/stdc++.h> using namespace std; int main(){ vector<int> bills = {5,5,10,10,20}; int five=0, ten=0; for (int b : bills) { if (b == 5) ++five; else if (b == 10) { if (!five) { cout << "FAIL\n"; return 0; } --five; ++ten; } else { if (ten && five) { --ten; --five; } else if (five >= 3) five -= 3; else { cout << "FAIL\n"; return 0; } } } cout << "OK\n"; }
Summary — Module 32
Greedy algorithms make the locally best choice at every step and never look back. They are short, fast, and beautiful — when they work. Activity selection (sort by finish time), Huffman coding (combine smallest), and fractional knapsack (sort by ratio) are the canonical wins. But for 0/1 knapsack and arbitrary coin systems greedy fails, and the only safe practice is to prove correctness by an exchange argument before trusting any greedy.