Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
Lose every unimportant contest for free (each loss ADDS its luck), and among the important contests lose the k with the largest luck and win the rest (winning SUBTRACTS luck). Sort the important contests by luck descending, add the top k, subtract the others. O(n log n) from the sort.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you separate the unconstrained part (lose all unimportant contests) from the constrained part (at most k important losses), and then greedily assign the k losses to the highest-luck important contests.
Where candidates slip
Forgetting that unimportant contests should all be lost regardless of k, or flipping the sign so winning adds luck — both silently produce a wrong total.
How to stand out
State the exchange argument: if a loss were spent on a smaller important contest while a larger one was won, swapping them increases the total — so the k losses must go to the largest luck values.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachFirst decision previewEach contest: L luck, T importanceImportant (T = 1)?NoYesExplore the full treeCollapse the tree
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
Brute Force — Try Every Choice of Which Important Contests to Lose
1Hint 1Explore hintHide hint
Unimportant contests are easy — what's the only sensible thing to do with a loss that costs nothing and only adds luck?
2Hint 2Explore hintHide hint
For the important contests, the real decision is which subset (of size ≤ k) to lose. How many such subsets are there?
3Hint 3Explore hintHide hint
Enumerate every valid subset of important contests to lose, compute the resulting balance, and keep the maximum. Correct, but exponential in the number of important contests.
A · Core idea
Strip out the easy half first: every unimportant contest should be lost, contributing +L with no constraint. That part needs no search.
Where the real search lives. The only genuine decision is which important contests to lose, subject to losing at most k of them. A brute force enumerates every subset of the important contests with size ≤ k, treats those as losses (+L) and the rest as wins (−L), and keeps the maximum balance.
Why it's correct but wasteful. Trying all subsets is guaranteed to find the best assignment, but there are up to 2^m subsets where m is the count of important contests. Even though n ≤ 100 here, this is exponential and pointless once you spot the structure.
The structure it reveals. Among all size-k subsets, the one that maximises the gain is obviously the k largest luck values — losing a big contest adds more than losing a small one, and being forced to win a small one subtracts less than winning a big one. That single observation replaces the entire subset search with a sort.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
k = 1, important luck = [5, 2]lose none
win both: −5 −2 = −7
best = −7lose the larger (5) → +3 from the important contestsC · Code with teaching comments
Reference implementation previewpublic int luckBalance(int k, int[][] contests) { List<Integer> important = new ArrayList<>(); int base = 0; for (int[] c : contests) { if (c[1] == 0) base += c[0];Explore the full codeHide the full code
D · Why it works and complexityO(2^m · m) timeO(m) spaceOpen the invariant and proofExplore the proofHide the proof
The brute force only struggles on the important contests, and even there the answer is monotone: bigger luck always prefers to be lost. Monotone preference under a count cap is the textbook signal to sort and take the top k.
ComplexityO(2^m · m) time, O(m) space — There are up to 2^m subsets of the m important contests (filtered to size ≤ k), and scoring each costs O(m). Unimportant contests are summed once in O(n).
E · Edge cases and pitfalls3Putting unimportant contests into the subset search — they have no cap, so they don't belong there.Explore edge casesHide edge cases
Pitfalls
- Putting unimportant contests into the subset search — they have no cap, so they don't belong there.
- Counting subsets larger than k — violates the constraint.
- Exponential blow-up — only viable for tiny m; use it to motivate the greedy.
Approach 2 of 2 · Optimal
Greedy — Sort Important Descending, Lose the Top k (Optimal)
1Hint 1Explore hintHide hint
Unimportant contests have no cap and only help — what should you always do with them?
2Hint 2Explore hintHide hint
Now you have a budget of k losses to spend on important contests. Which important contests give the biggest swing when lost rather than won?
3Hint 3Explore hintHide hint
Sort the important luck values descending. Add the first k (losses), subtract the rest (forced wins), and add the full sum of unimportant luck. Done in one pass after the sort.
A · Core idea
Split the contests into two buckets. Unimportant contests are unconstrained, so lose every one of them — add all their luck to a running base. Important contests are capped at k losses, so collect their luck values, sort them descending, lose (add) the first k, and win (subtract) the remaining ones.
Why every unimportant contest is lost. Losing adds +L with L ≥ 1, and there's no limit on unimportant losses. There is literally never a reason to win one — winning would subtract luck we could instead gain. So they contribute their entire sum to the answer with no decision to make.
Why the k losses go to the largest important contests — the exchange argument. Suppose an optimal solution loses a smaller important contest with luck a while winning a larger one with luck b > a. The two together contribute +a − b. Swap them — lose b, win a — and they now contribute +b − a, which is larger by 2(b − a) > 0. So any solution that doesn't spend its losses on the largest values can be strictly improved. Therefore the optimal assignment loses exactly the k largest important contests. Sorting descending and taking the prefix of length k realises this directly.
Why the remaining important contests are subtracted. Once the k loss-slots are used up, every other important contest must be won, and winning subtracts its luck. There's no third option — the cap forces it. Getting this sign wrong is the most common silent bug here.
The k ≥ m corner. If there are fewer important contests than k, every important contest is lost and none is won — the 'win' branch simply never executes. The index check i < k handles this without a special case.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
k = 3, contests = [[5,1],[2,1],[1,1],[8,1],[10,0],[5,0]]Array state
i=0 (8)
i < 3 → lose, +8
29C · Code with teaching comments
D · Why it works and complexityO(n log n) timeO(n) spaceOpen the invariant and proofExplore the proofHide the proof
The problem factors cleanly into a free part (sum all unimportant luck) and a capped part (a top-k selection among important contests). Spotting that the cap only binds on one bucket is what reduces a 2^m search to a sort. 'Free choices + one capped greedy selection' is a recurring decomposition — isolate the constraint, then sort within it.
ComplexityO(n log n) time, O(n) space — Bucketing the contests is O(n); sorting the important list is O(m log m) ≤ O(n log n) and dominates. We store the important values → O(n) space.
E · Edge cases and pitfalls6k = 0, important = [3, 7] — No losses allowed; every important contest is won → −3 − 7 = −10 added to base.Explore edge casesHide edge cases
Edge cases handled
k = 0, important = [3, 7]No losses allowed; every important contest is won → −3 − 7 = −10 added to base.k ≥ number of important contestsEvery i < k, so all important contests are lost — the win branch never runs.no important contestsThe important list is empty; luck is just the sum of all unimportant losses.
Pitfalls
- Sorting ascending — you'd lose the smallest important contests and win the largest, minimising instead of maximising.
- Flipping the sign so wins add and losses subtract — inverts the whole answer.
- Applying the cap k to unimportant contests — they have no cap and should all be lost.
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewpublic int luckBalance(int k, int[][] contests) { List<Integer> important = new ArrayList<>(); int luck = 0; for (int[] c : contests) { if (c[1] == 0) {Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Sorting important contests ascending
You must spend the k losses on the LARGEST important contests; ascending order loses the smallest ones and wins the biggest, which minimises luck.
Explore the code comparisonHide the code comparison
Sorting important contests ascending
You must spend the k losses on the LARGEST important contests; ascending order loses the smallest ones and wins the biggest, which minimises luck.
Don't write this
important.sort(Comparator.naturalOrder());
for (int i = 0; i < important.size(); i++) {
luck += (i < k) ? important.get(i) : -important.get(i);
}Write this instead
important.sort(Comparator.reverseOrder());
for (int i = 0; i < important.size(); i++) {
luck += (i < k) ? important.get(i) : -important.get(i);
}2Inverting the win/lose sign
Losing ADDS luck and winning SUBTRACTS it. Swapping the signs makes the algorithm minimise the balance.
Explore the code comparisonHide the code comparison
Inverting the win/lose sign
Losing ADDS luck and winning SUBTRACTS it. Swapping the signs makes the algorithm minimise the balance.
Don't write this
luck += -amount if i < k else amountWrite this instead
luck += amount if i < k else -amount3Applying the cap to unimportant contests
Only important contests are capped at k losses. Unimportant contests have no limit and must all be lost for full luck.
Explore the code comparisonHide the code comparison
Applying the cap to unimportant contests
Only important contests are capped at k losses. Unimportant contests have no limit and must all be lost for full luck.
Don't write this
all_luck = sorted([amount for amount, _ in contests], reverse=True)
luck = sum(a if i < k else -a for i, a in enumerate(all_luck))Write this instead
for amount, is_important in contests:
if is_important == 0:
luck += amount
else:
important.append(amount)Revise
Carry the pattern into the interview
Pattern to remember
Greedy with a constrained budget of 'losses' (sort important desc, lose the biggest k)
Code pattern to memorize
luck = 0
for (L, important) in contests:
if not important: luck += L # lose all unimportant — free
else: importantLuck.append(L)
sort importantLuck descending
for i, L in enumerate(importantLuck):
luck += L if i < k else -L # lose top k, win the restRules to remember
- 1Always lose unimportant contests — there's no cap on them and each loss only adds luck.
- 2For important contests you may lose at most k; spend those k losses on the largest luck values for maximum gain.
- 3Sort important contests descending and split at index k: indices
< kare losses (+L), the rest are wins (-L). - 4Winning a contest subtracts its luck — losing adds it. Get the sign right or the answer flips.
- 5If there are fewer than k important contests, you simply lose them all (you never have to win any).
Use this pattern when
- You have a hard cap on how many 'costly' choices you can make and want to maximise a sum
- Each item contributes +value if chosen one way, −value the other, with a count limit on the favourable choice
- Items split into 'free' (no limit) and 'capped' groups
- The favourable action should clearly go to the largest-magnitude items
Choose another pattern when
- The cap interacts across items (losing one changes another's value) → greedy breaks, think DP
- You must lose EXACTLY k, not at most k → still greedy here, but watch the boundary
- Multiple resource caps at once → multi-dimensional optimisation
- Values can be negative or the +/− rule is conditional → re-derive the exchange argument
Interview takeaway
When a limited 'budget' of favourable choices buys you +value vs −value per item, spend the budget on the largest items. Sort descending, take the top k. This 'biggest-first under a count cap' move shows up across greedy problems.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternGreedy top-k under a count cap. Decompose into a free bucket (always take the favourable action) and a capped bucket (sort by magnitude, take the best k). Same skeleton appears wherever a limited budget of favourable choices should go to the largest-magnitude items.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Mark and ToysAnother sort-and-prefix greedy — buy the cheapest items under a budget.
- Maximum Subarray (Kadane)Coming soonDifferent lens, same goal of maximising a running total of signed contributions.
- Greedy FloristSort and assign costliest items to minimise a multiplier — another sort-by-magnitude greedy.
- Maximize Toys / Assign CookiesComing soonCapped greedy matching with a sorted prefix.