Lesson 31 of 50 intermediate

More Greedy Problems

Practice the greedy mindset with classic problems

Open interactive version (quiz + challenge)

Real-world analogy

Packing a suitcase for vacation is a greedy problem. You have limited space and want to bring the most valuable items. The greedy approach? Rank items by value-per-size and start packing from the most valuable per unit of space. Heaviest textbook? Maybe skip it. Lightest snack bag that brings you joy? Definitely pack it first!

What is it?

This lesson expands your greedy algorithm toolkit with more classic problems. We explore the fractional knapsack (take the most valuable items per unit weight), the minimum platforms problem (handle overlapping intervals), and develop a systematic approach to recognize when greedy algorithms are applicable and how to prove they work.

Real-world relevance

Fractional knapsack is used in investment portfolio optimization (allocate budget to highest-return assets), in cargo loading (fill a truck with the most valuable goods), and in bandwidth allocation (give more bandwidth to higher-priority traffic). The minimum platforms problem is literally used by train stations and airports to plan infrastructure!

Key points

Code example

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    // Fractional Knapsack
    int n, W;
    cin >> n >> W;
    vector<pair<double, pair<int,int>>> items(n); // {ratio, {value, weight}}

    for (int i = 0; i < n; i++) {
        int v, w;
        cin >> v >> w;
        double ratio = (double)v / w;
        items[i] = {ratio, {v, w}};
    }

    // Sort by value/weight ratio in descending order
    sort(items.begin(), items.end(), greater<pair<double, pair<int,int>>>());

    double totalValue = 0;
    int remainingCapacity = W;

    for (int i = 0; i < n && remainingCapacity > 0; i++) {
        int w = items[i].second.second;
        int v = items[i].second.first;

        if (w <= remainingCapacity) {
            // Take the whole item
            totalValue += v;
            remainingCapacity -= w;
            cout << "Take all of item (value=" << v << ", weight=" << w << ")\n";
        } else {
            // Take a fraction
            double fraction = (double)remainingCapacity / w;
            totalValue += v * fraction;
            cout << "Take " << fixed << setprecision(2) << fraction * 100
                 << "% of item (value=" << v << ", weight=" << w << ")\n";
            remainingCapacity = 0;
        }
    }

    cout << "Total value: " << fixed << setprecision(2) << totalValue << "\n\n";

    // Minimum Platforms
    int trains;
    cin >> trains;
    vector<int> arrivals(trains), departures(trains);
    for (int i = 0; i < trains; i++) cin >> arrivals[i];
    for (int i = 0; i < trains; i++) cin >> departures[i];

    sort(arrivals.begin(), arrivals.end());
    sort(departures.begin(), departures.end());

    int platforms = 0, maxPlatforms = 0;
    int i = 0, j = 0;
    while (i < trains) {
        if (arrivals[i] <= departures[j]) {
            platforms++;
            maxPlatforms = max(maxPlatforms, platforms);
            i++;
        } else {
            platforms--;
            j++;
        }
    }
    cout << "Minimum platforms needed: " << maxPlatforms << "\n";

    return 0;
}

Line-by-line walkthrough

  1. 1. double ratio = (double)v / w; — we calculate the value-per-unit-weight ratio for each item. The (double) cast ensures we get decimal division, not integer division.
  2. 2. sort(items.begin(), items.end(), greater()); — we sort items by ratio in descending order. greater<> reverses the default ascending sort. Now the best value-per-weight items come first.
  3. 3. if (w <= remainingCapacity) — if the item fits entirely in our remaining capacity, take the whole thing. Add its full value and reduce capacity by its weight.
  4. 4. double fraction = (double)remainingCapacity / w; — if the item does not fit entirely, we take a fraction. If we have 20 kg left and the item weighs 30 kg, we take 20/30 = 2/3 of it.
  5. 5. totalValue += v * fraction; — we add the proportional value. If we take 2/3 of an item worth 120, we get 80 value.
  6. 6. sort(arrivals...) and sort(departures...) — for minimum platforms, we sort arrivals and departures separately. Then we process events in time order using two pointers.
  7. 7. if (arrivals[i] <= departures[j]) — if the next event is an arrival (happens before or at the same time as the next departure), we need one more platform. Otherwise, a train left and we free a platform.

Spot the bug

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n = 3, W = 50;
    int values[] = {60, 100, 120};
    int weights[] = {10, 20, 30};

    // Sort by value (descending)
    // (simplified: just process in order)
    double totalValue = 0;
    int remaining = W;

    for (int i = 0; i < n; i++) {
        if (weights[i] <= remaining) {
            totalValue += values[i];
            remaining -= weights[i];
        }
    }

    cout << fixed << setprecision(2) << totalValue << "\n";
    return 0;
}
Need a hint?
There are two problems: the sorting criterion and the handling of the last item. What if an item does not fully fit — should we skip it entirely?
Show answer
Two bugs! First, items are not sorted by value/weight ratio. Processing in arbitrary order may not give the optimal result. Second, when an item does not fully fit, the code skips it entirely instead of taking a fraction. Fix: sort by value/weight ratio descending, and add an else branch that takes a fraction: totalValue += values[i] * ((double)remaining / weights[i]); remaining = 0;

Explain like I'm 5

Imagine you have a bag and a table full of candy. Each candy has a yumminess score and a size. Your bag can only hold so much. What do you do? You figure out which candy gives you the most yumminess for its size. A tiny super-yummy candy? Take it first! A huge candy that is only okay? Maybe just take a piece of it to fill the last bit of space. You are always picking the yummiest-per-bite option. That is fractional knapsack — always take the best deal first!

Fun fact

The knapsack problem is named after the scenario of a thief who has a backpack (knapsack) of limited capacity and must choose which items to steal to maximize total value. The fractional version (where you can cut items) is solvable greedily, but the 0/1 version (whole items only) is one of the famous 'NP-hard' problems — no known fast algorithm exists for it! This distinction between 'can take fractions' and 'must take whole items' is one of the most beautiful examples in computer science of how a small change in problem definition can make the difference between easy and incredibly hard.

Hands-on challenge

Implement the fractional knapsack problem. Read n items (each with value and weight) and a knapsack capacity W. Output the maximum total value you can carry. Test with: W=50, items: (60,10), (100,20), (120,30). Expected output: 240.00. Then try: W=15, items: (10,5), (20,10), (30,15). What is the answer?

More resources

Open interactive version (quiz + challenge) ← Back to course: CP Zero to Hero