IPO
Difficulty: Hard Source: NeetCode
Problem
Suppose LeetCode will start its IPO soon. In order to sell a good price of its shares to venture capitalists, LeetCode would like to work on some projects to increase its capital before the IPO. Since it has limited resources, it can only finish at most
kdistinct projects before the IPO. Help LeetCode design the best way to maximize its total capital after finishing at mostkdistinct projects.You are given
nprojects where theith project has a pure profitprofits[i]and a minimum capital ofcapital[i]required to start it.Initially, you have
wcapital. When you finish a project, you will obtain its pure profit and the profit will be added to your total capital. Pick at mostkdistinct projects from given projects to maximize your final capital, and return the maximized capital.Example 1: Input:
k = 2,w = 0,profits = [1, 2, 3],capital = [0, 1, 1]Output:4Example 2: Input:
k = 3,w = 0,profits = [1, 2, 3],capital = [0, 1, 2]Output:6Constraints:
1 <= k <= 10^50 <= w <= 10^9n == profits.length == capital.length1 <= n <= 10^50 <= profits[i] <= 10^40 <= capital[i] <= 10^9
Prerequisites
Before attempting this problem, you should be comfortable with:
- Greedy algorithms — always taking the most profitable currently affordable project
- Two heaps pattern — one heap for locked projects (min by capital), one for available projects (max by profit)
- Min-heap and max-heap — using Python’s
heapqwith negation for max-heap behavior
1. Brute Force — Scan All Projects Each Round
Intuition
For each of the k rounds, scan all projects, find all that we can currently afford, pick the one with the highest profit, collect it, and add the profit to our capital. This is correct but slow for large inputs.
Algorithm
- Repeat at most
ktimes: a. Find all projects wherecapital[i] <= w. b. If none exist, break. c. Pick the project with the maximum profit. Add its profit tow. Mark it as done. - Return
w.
Solution
def findMaximizedCapital(k, w, profits, capital):
n = len(profits)
done = [False] * n
for _ in range(k):
best_profit = -1
best_idx = -1
for i in range(n):
if not done[i] and capital[i] <= w:
if profits[i] > best_profit:
best_profit = profits[i]
best_idx = i
if best_idx == -1:
break # can't afford any project
done[best_idx] = True
w += best_profit
return w
print(findMaximizedCapital(2, 0, [1, 2, 3], [0, 1, 1])) # 4
print(findMaximizedCapital(3, 0, [1, 2, 3], [0, 1, 2])) # 6
print(findMaximizedCapital(1, 0, [1, 2, 3], [1, 1, 2])) # 0 (can't afford any)
Complexity
- Time:
O(n * k)— scan all n projects for each of k rounds - Space:
O(n)
2. Two Heaps — Greedy Optimal
Intuition
We want to greedily pick the highest-profit affordable project in each round. The key insight: sort projects by capital requirement. Use a min-heap by capital (locked) to efficiently release projects as our wallet grows. Each round, push all newly affordable projects (capital ≤ w) onto a max-heap by profit (available). Then pop the most profitable available project, collect its profit, and repeat.
This is essentially a “sweep” — as w grows, more projects unlock and flow from the locked heap to the available heap.
Algorithm
- Build
locked = [(capital[i], profits[i]) for i in range(n)]as a min-heap. availableis a max-heap (negate profits) of projects we can currently afford.- Repeat at most
ktimes: a. Whilelockedis non-empty andlocked[0][0] <= w, pop it and push-profitontoavailable. b. Ifavailableis empty, break (can’t afford anything). c. Pop the most profitable fromavailable, add its profit tow. - Return
w.
Solution
import heapq
def findMaximizedCapital(k, w, profits, capital):
# Min-heap of locked projects sorted by capital requirement
locked = list(zip(capital, profits))
heapq.heapify(locked)
available = [] # Max-heap of profits for affordable projects (negated)
for _ in range(k):
# Unlock all projects we can now afford
while locked and locked[0][0] <= w:
cap, profit = heapq.heappop(locked)
heapq.heappush(available, -profit)
if not available:
break # Can't unlock any new project
# Take the highest-profit available project
w += -heapq.heappop(available)
return w
print(findMaximizedCapital(2, 0, [1, 2, 3], [0, 1, 1])) # 4
print(findMaximizedCapital(3, 0, [1, 2, 3], [0, 1, 2])) # 6
print(findMaximizedCapital(1, 0, [1, 2, 3], [1, 1, 2])) # 0
print(findMaximizedCapital(2, 1, [2, 3, 5], [0, 1, 2])) # 9: pick profit 3 (w=4), then profit 5 (w=9)
Complexity
- Time:
O(n log n + k log n)— heapify is O(n log n); each of k rounds does O(log n) heap ops - Space:
O(n)
Common Pitfalls
Not unlocking all affordable projects before picking. In each round, you should push every project with capital <= w onto the available heap before selecting. If you push only one and then select, you might miss a higher-profit project that was also affordable.
Confusing which heap uses negation. The locked heap is a min-heap by capital (no negation needed). The available heap is a max-heap by profit (negate profits). Keep this straight or you’ll pick the wrong project each round.
Stopping too early when locked is empty but available is not. Just because there are no more projects to unlock doesn’t mean we’re done — we might still have profitable projects sitting in available from previous rounds. Only break when available is empty.