We have a set of projects, each requiring some minimum capital to start and yielding a certain profit upon completion. We start with w capital and can complete at most k projects. After completing a project, its profit gets added to our capital, potentially unlocking more expensive projects. The goal is to maximize the final capital.
A greedy rule solves this: at each step, among all projects we can currently afford, pick the one with the highest profit. That gives us the most capital for future rounds, which in turn unlocks the most options. The challenge is doing this efficiently. With up to 10^5 projects and 10^5 rounds, we need a data structure that returns the most profitable affordable project quickly.
1 <= k <= 10^5 and 1 <= n <= 10^5 → We might iterate up to k times, and each iteration needs to efficiently find the best project. A naive O(n) scan per iteration gives O(k * n) = O(10^10), which is too slow.0 <= w <= 10^9 and 0 <= capital[i] <= 10^9 → Capital values can be very large, so we can't use them as array indices. No bucket sort tricks.0 <= profits[i] <= 10^4 → Profits are non-negative, so completing more projects never decreases our capital. This is why running for the full k rounds (when projects remain affordable) is safe.The greedy rule says: always pick the most profitable project we can afford. A direct implementation scans all projects each round, finds the one with the highest profit among those whose capital requirement is at most our current capital, completes it, and repeats up to k times.
Each project can only be completed once, so we track which projects have been used with a boolean array.
used of size n, initialized to false.capital[i] <= w, find the one with the maximum profits[i].w.w.used array takes O(n) space.This approach is correct but too slow for large inputs, because it re-scans every project on every round. The next approach removes the repeated scanning by sorting projects by capital and using a max-heap to retrieve the best affordable project in logarithmic time.
Once a project becomes affordable, it stays affordable, because our capital only increases. So instead of re-scanning every project each round, we sort projects by their capital requirement and use a pointer to track which ones we have unlocked so far.
The plan: sort all projects by capital. Maintain a max-heap of profits for all projects we can currently afford. Each round, push any newly affordable projects into the max-heap (the previous round increased our capital, so more may have become reachable), then pop the top to get the most profitable one. The pointer never moves backward because capital only grows.
Picking the most profitable affordable project first is safe because of an exchange argument. Suppose an optimal solution completes some other affordable project P first instead of the highest-profit affordable project H. Profits are non-negative, so swapping P for H in that first slot gives capital that is at least as high at every later step. The set of projects affordable after taking H is therefore a superset of those affordable after taking P, so any remaining choices the optimal solution made are still available. The swapped solution earns at least as much, so the greedy choice loses nothing.
The cost stays low because each project is pushed into and popped from the heap at most once, and the pointer only moves forward. Across all k rounds the total heap work is O(n log n).
(capital[i], profits[i]) and sort by capital ascending.idx = 0 and a max-heap (empty).idx < n and projects[idx].capital <= w, push projects[idx].profit into the max-heap and increment idx.w.w.