2Brute2Best
🔥0
02

Sort + exchange argument (Greedy Sub-pattern)

Sort items by one key so that swapping any two neighbours out of order can't improve the answer, then take them in that order.

Signals in the problem statement
  • ›

    items can be split (fractional), or each takes one time unit with a deadline

  • ›

    "send exactly half to A and half to B"

  • ›

    people/jobs described by two numbers that must be ordered

What makes it different

Fractional knapsack → value / weight. Two cities → costA − costB. Deadlines → sort by deadline and keep the best profits in a min-heap. Queue by height → tallest first, then insert at index k.

Questions

ordered from “learn the idea” to “recognise it without help”
  1. 1
    Fractional Knapsack
    Basic Anchor·GeeksforGeeks
    Easy
  2. 2
    Two City Scheduling
    Basic Reinforcement·LeetCode #1029
    Medium
  3. 3
    Job Sequencing Problem
    Slight Variation·GeeksforGeeks
    Medium
  4. 4
    Queue Reconstruction by Height
    Medium·LeetCode #406
    Medium

Template

exchange-template.cpp
// The comparator IS the algorithm. Prove it with a swap: if a should come before b,
// swapping them in any answer must not make it better.

// Fractional knapsack: best value per weight first; compare ratios without division.
double fractional(vector<int> val, vector<int> wt, long long cap) {
    int n = val.size();
    vector<int> idx(n);
    iota(idx.begin(), idx.end(), 0);
    sort(idx.begin(), idx.end(), [&](int a, int b) {
        return 1LL * val[a] * wt[b] > 1LL * val[b] * wt[a];  // val[a]/wt[a] > val[b]/wt[b]
    });
    double total = 0;
    for (int i : idx) {
        if (cap == 0) break;
        long long take = min<long long>(cap, wt[i]);
        total += (double)val[i] * take / wt[i];
        cap -= take;
    }
    return total;
}

// Deadlines, one time unit each: by deadline, keep the best profits in a min-heap.
long long maxProfitJobs(vector<pair<int,int>> jobs) {         // {deadline, profit}
    sort(jobs.begin(), jobs.end());
    priority_queue<int, vector<int>, greater<int>> kept;
    for (auto& [d, p] : jobs) {
        kept.push(p);
        if ((int)kept.size() > d) kept.pop();                // too many jobs by time d: drop the cheapest
    }
    long long sum = 0;
    while (!kept.empty()) { sum += kept.top(); kept.pop(); }
    return sum;
}
Remember this · Greedy
Trigger
Maximise count / minimise cost with a natural "best next step", sorting makes the choice obvious, reachability.
Think
Is there a choice I can always make safely without looking ahead?
Key question
Why can't the greedy choice be worse than the optimal one? (Try to break it with a small example.)