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”- 1Fractional KnapsackWalkthroughEasyBasic Anchor·GeeksforGeeks
- 2Two City SchedulingWalkthroughMediumBasic Reinforcement·LeetCode #1029
- 3Job Sequencing ProblemWalkthroughMediumSlight Variation·GeeksforGeeks
- 4Queue Reconstruction by HeightWalkthroughMediumMedium·LeetCode #406
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.)