2412. Minimum Money Required Before Transactions
Problem Description
You have a list of transactions that you need to complete. Each transaction is represented as [cost, cashback]
, where:
cost
is the amount of money you need to have to perform the transactioncashback
is the amount of money you receive back after completing the transaction
The key rules are:
- Each transaction must be completed exactly once
- To complete a transaction with cost
cost_i
, you must have at leastcost_i
money available - After completing a transaction, your money changes from
money
tomoney - cost + cashback
- You can complete the transactions in any order you choose
The problem asks: What is the minimum initial amount of money you need to guarantee that you can complete ALL transactions, regardless of the order you choose to complete them?
For example, if you have a transaction [10, 3]
:
- You need at least 10 money to start this transaction
- After completing it, you'll have
money - 10 + 3 = money - 7
(you lose 7 money overall)
The challenge is finding the minimum starting money that works for ANY possible ordering of the transactions, since we want to ensure we can complete all transactions no matter which order we pick.
Intuition
Let's think about what happens with our money as we complete transactions. Each transaction [cost, cashback]
has a net effect of cashback - cost
on our money. If cost > cashback
, we lose money overall (negative profit). If cashback > cost
, we gain money (positive profit).
The key insight is that we want to minimize our initial money requirement. To do this optimally, we should complete all losing transactions (where cost > cashback
) first, then complete the profitable ones. Why? Because profitable transactions increase our money, making it easier to afford subsequent transactions.
So first, let's calculate the total loss from all losing transactions: s = sum(max(0, cost - cashback))
. This represents how much money we'll lose from all the bad transactions.
Now, which transaction should be the last one? This is crucial because the last transaction determines our peak money requirement. We need to consider two cases:
-
If the last transaction is a losing one
(cost > cashback)
: We need enough money to cover all previous losses plus this transaction's cost. But wait - this transaction's loss is already included ins
, so we actually needs - (cost - cashback) + cost = s + cashback
. -
If the last transaction is a profitable one
(cost <= cashback)
: We need enough money to cover all previous losses (the fulls
) plus this transaction's cost, giving uss + cost
.
Since we don't know which order is actually worst-case, we try each transaction as the potential last one and take the maximum requirement. This ensures we have enough money for any possible ordering.
The formula max(ans, s + b)
when a > b
and max(ans, s + a)
otherwise captures exactly this logic - finding the worst-case initial money needed across all possible final transactions.
Solution Approach
The implementation follows a greedy strategy based on our intuition about handling losing transactions first.
Step 1: Calculate total losses
s = sum(max(0, a - b) for a, b in transactions)
We iterate through all transactions and accumulate the net loss from each losing transaction. For a transaction [a, b]
:
- If
a > b
(losing transaction), we adda - b
to our sum - If
a <= b
(profitable transaction), we add0
(usingmax(0, a - b)
)
This gives us s
, the total amount we'll lose from all losing transactions.
Step 2: Find the worst-case scenario
ans = 0
for a, b in transactions:
if a > b:
ans = max(ans, s + b)
else:
ans = max(ans, s + a)
We consider each transaction as potentially being the last one executed:
-
When
a > b
(losing transaction as last):- The loss
a - b
from this transaction is already included ins
- To execute this as the last transaction, we need
s - (a - b) + a = s + b
money - We update answer with
max(ans, s + b)
- The loss
-
When
a <= b
(profitable transaction as last):- This transaction wasn't included in
s
since it's profitable - To execute this as the last transaction after all losses, we need
s + a
money - We update answer with
max(ans, s + a)
- This transaction wasn't included in
The maximum value across all these scenarios gives us the minimum initial money needed to guarantee we can complete all transactions in some order.
Time Complexity: O(n)
where n is the number of transactions (two passes through the array)
Space Complexity: O(1)
as we only use a few variables
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with transactions: [[3, 0], [2, 1], [5, 8]]
Step 1: Calculate total losses
First, we identify which transactions lose money:
- Transaction
[3, 0]
: cost=3, cashback=0 โ loses 3-0 = 3 money - Transaction
[2, 1]
: cost=2, cashback=1 โ loses 2-1 = 1 money - Transaction
[5, 8]
: cost=5, cashback=8 โ gains 8-5 = 3 money (profitable!)
Total losses s = 3 + 1 + 0 = 4
Step 2: Consider each transaction as potentially the last one
Now we check how much initial money we'd need if each transaction were executed last:
-
If
[3, 0]
is last (losing transaction):- We need to handle the other transactions first, losing money from
[2, 1]
- The loss from
[3, 0]
is already ins=4
, so we need:s + cashback = 4 + 0 = 4
- This means: start with 4 โ after other transactions we'd have at least 3 โ can afford cost of 3
- We need to handle the other transactions first, losing money from
-
If
[2, 1]
is last (losing transaction):- The loss from
[2, 1]
is already ins=4
, so we need:s + cashback = 4 + 1 = 5
- This means: start with 5 โ after other transactions we'd have at least 2 โ can afford cost of 2
- The loss from
-
If
[5, 8]
is last (profitable transaction):- This wasn't included in
s
, so we need:s + cost = 4 + 5 = 9
- This means: start with 9 โ lose 4 from the losing transactions โ have 5 left โ can afford cost of 5
- This wasn't included in
Step 3: Take the maximum
The worst case is max(4, 5, 9) = 9
Verification: Let's verify that 9 works for any order:
- Order 1:
[3,0]
โ[2,1]
โ[5,8]
: 9โ6โ5โ10 โ - Order 2:
[2,1]
โ[3,0]
โ[5,8]
: 9โ8โ5โ10 โ - Order 3:
[5,8]
โ[3,0]
โ[2,1]
: 9โ12โ9โ8 โ - Order 4:
[5,8]
โ[2,1]
โ[3,0]
: 9โ12โ11โ8 โ - Order 5:
[3,0]
โ[5,8]
โ[2,1]
: 9โ6โ9โ8 โ - Order 6:
[2,1]
โ[5,8]
โ[3,0]
: 9โ8โ11โ8 โ
All orderings work with initial money of 9, confirming our answer!
Solution Implementation
1class Solution:
2 def minimumMoney(self, transactions: List[List[int]]) -> int:
3 # Calculate total net loss from all transactions where cost exceeds cashback
4 # This represents the minimum amount we'll definitely lose
5 total_net_loss = sum(max(0, cost - cashback) for cost, cashback in transactions)
6
7 # Initialize the minimum money needed
8 min_money_needed = 0
9
10 # Check each transaction as if it were the last one performed
11 # This ensures we have enough money for any ordering of transactions
12 for cost, cashback in transactions:
13 if cost > cashback:
14 # For losing transactions: need total losses plus this transaction's cashback
15 # (since we get cashback after paying, we need it available before)
16 min_money_needed = max(min_money_needed, total_net_loss + cashback)
17 else:
18 # For gaining/neutral transactions: need total losses plus this transaction's cost
19 # (since we gain/break even, we just need enough to start this transaction)
20 min_money_needed = max(min_money_needed, total_net_loss + cost)
21
22 return min_money_needed
23
1class Solution {
2 public long minimumMoney(int[][] transactions) {
3 // Calculate total net loss from all losing transactions
4 // A transaction causes a net loss when cost > cashback
5 long totalNetLoss = 0;
6 for (int[] transaction : transactions) {
7 int cost = transaction[0];
8 int cashback = transaction[1];
9 // Only add the net loss (cost - cashback) if it's positive
10 totalNetLoss += Math.max(0, cost - cashback);
11 }
12
13 // Find the minimum initial money needed to complete all transactions
14 long minimumInitialMoney = 0;
15 for (int[] transaction : transactions) {
16 int cost = transaction[0];
17 int cashback = transaction[1];
18
19 if (cost > cashback) {
20 // For losing transactions: need total loss from other transactions + cashback from this one
21 // This ensures we have enough money before executing this transaction
22 minimumInitialMoney = Math.max(minimumInitialMoney, totalNetLoss + cashback);
23 } else {
24 // For profitable/neutral transactions: need total loss + full cost of this transaction
25 // Since we don't lose money on this transaction, we need its full cost upfront
26 minimumInitialMoney = Math.max(minimumInitialMoney, totalNetLoss + cost);
27 }
28 }
29
30 return minimumInitialMoney;
31 }
32}
33
1class Solution {
2public:
3 long long minimumMoney(vector<vector<int>>& transactions) {
4 // Calculate total net loss from all losing transactions
5 // A losing transaction is one where cost > cashback
6 long long totalLoss = 0;
7 for (auto& transaction : transactions) {
8 int cost = transaction[0];
9 int cashback = transaction[1];
10 // Only add the net loss (cost - cashback) if it's positive
11 totalLoss += max(0, cost - cashback);
12 }
13
14 // Find the minimum initial money needed
15 long long minInitialMoney = 0;
16 for (auto& transaction : transactions) {
17 int cost = transaction[0];
18 int cashback = transaction[1];
19
20 if (cost > cashback) {
21 // For losing transactions: we need totalLoss + cashback
22 // The cashback is what we need to keep after all other losses
23 minInitialMoney = max(minInitialMoney, totalLoss + cashback);
24 } else {
25 // For non-losing transactions: we need totalLoss + cost
26 // We need the full cost on top of all losses from other transactions
27 minInitialMoney = max(minInitialMoney, totalLoss + cost);
28 }
29 }
30
31 return minInitialMoney;
32 }
33};
34
1/**
2 * Calculates the minimum amount of money needed to complete all transactions
3 * @param transactions - Array of transactions where each transaction is [cost, cashback]
4 * @returns The minimum initial money required
5 */
6function minimumMoney(transactions: number[][]): number {
7 // Calculate the total net loss from all losing transactions (where cost > cashback)
8 // This represents the guaranteed money we'll lose
9 const totalNetLoss: number = transactions.reduce(
10 (accumulator: number, [cost, cashback]: number[]) =>
11 accumulator + Math.max(0, cost - cashback),
12 0
13 );
14
15 let minimumRequired: number = 0;
16
17 // For each transaction, calculate the minimum money needed considering:
18 // - We need the total net loss from all losing transactions
19 // - Plus either the cashback (for losing transactions) or cost (for profitable transactions)
20 // This ensures we have enough money to perform this transaction last
21 for (const [cost, cashback] of transactions) {
22 if (cost > cashback) {
23 // For losing transactions: need total loss + cashback to ensure we can do this last
24 minimumRequired = Math.max(minimumRequired, totalNetLoss + cashback);
25 } else {
26 // For profitable/neutral transactions: need total loss + cost to ensure we can do this last
27 minimumRequired = Math.max(minimumRequired, totalNetLoss + cost);
28 }
29 }
30
31 return minimumRequired;
32}
33
Time and Space Complexity
The time complexity is O(n)
, where n
is the number of transactions. The algorithm iterates through the transactions list twice - once in the list comprehension to calculate the sum s
with sum(max(0, a - b) for a, b in transactions)
, and once in the for loop to find the maximum value. Each iteration performs constant-time operations (max
, subtraction, and comparison), resulting in O(n) + O(n) = O(n)
overall time complexity.
The space complexity is O(1)
. The algorithm only uses a fixed amount of extra space for variables s
and ans
, regardless of the input size. The list comprehension in the sum function is implemented as a generator expression, which doesn't create an intermediate list but instead computes values on-the-fly, maintaining constant space usage.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Misunderstanding the Problem Constraint
The Mistake: Many people initially think they need to find the optimal order of transactions that minimizes the required starting money. They might try complex ordering strategies or dynamic programming approaches.
Why It's Wrong: The problem asks for the minimum money needed to guarantee completion of ALL transactions for ANY order. We're not looking for the best order; we're looking for an amount that works regardless of order choice.
Correct Understanding: We need to find the worst-case scenario across all possible orderings and ensure we have enough money for that worst case.
Pitfall 2: Incorrectly Calculating Required Money for Each Transaction
The Mistake: When checking a transaction as the last one, using the wrong formula:
# WRONG: Just using the cost of the transaction
if cost > cashback:
min_money_needed = max(min_money_needed, total_net_loss + cost) # Wrong!
Why It's Wrong: For losing transactions, when we place them last, their loss is already counted in total_net_loss
. If we have total_net_loss
money and execute all losing transactions, we end with 0. To then execute a transaction [cost, cashback]
where cost > cashback
, we need exactly cashback
money (since after all losses we have total_net_loss - total_net_loss = 0
, and we need cost
to start, but the loss cost - cashback
was already accounted for).
Correct Approach:
if cost > cashback:
# The loss (cost - cashback) is already in total_net_loss
# So we need: total_net_loss - (cost - cashback) + cost = total_net_loss + cashback
min_money_needed = max(min_money_needed, total_net_loss + cashback)
else:
# Profitable transaction not in total_net_loss, need full cost on top
min_money_needed = max(min_money_needed, total_net_loss + cost)
Pitfall 3: Not Considering All Transactions as Potential Last Transactions
The Mistake: Only checking losing transactions or only checking profitable transactions as the last one:
# WRONG: Only considering losing transactions
for cost, cashback in transactions:
if cost > cashback:
min_money_needed = max(min_money_needed, total_net_loss + cashback)
# Missing the check for profitable transactions!
Why It's Wrong: Both losing and profitable transactions could require the maximum initial money when placed last. A profitable transaction with very high cost might require more initial money than any losing transaction.
Example:
- Transactions:
[[10, 5], [100, 101]]
- Total loss:
10 - 5 = 5
- Checking
[10, 5]
last: need5 + 5 = 10
- Checking
[100, 101]
last: need5 + 100 = 105
โ This is the maximum!
Correct Approach: Always check every transaction as a potential last transaction, regardless of whether it's losing or profitable.
Consider the classic dynamic programming of longest increasing subsequence:
Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.
For example, the length of LIS for [50, 3, 10, 7, 40, 80]
is 4
and LIS is
[3, 7, 40, 80]
.
What is the recurrence relation?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Donโt Miss This!