Facebook Pixel

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 transaction
  • cashback is the amount of money you receive back after completing the transaction

The key rules are:

  1. Each transaction must be completed exactly once
  2. To complete a transaction with cost cost_i, you must have at least cost_i money available
  3. After completing a transaction, your money changes from money to money - cost + cashback
  4. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. 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 in s, so we actually need s - (cost - cashback) + cost = s + cashback.

  2. If the last transaction is a profitable one (cost <= cashback): We need enough money to cover all previous losses (the full s) plus this transaction's cost, giving us s + 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.

Learn more about Greedy and Sorting patterns.

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 add a - b to our sum
  • If a <= b (profitable transaction), we add 0 (using max(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 in s
    • To execute this as the last transaction, we need s - (a - b) + a = s + b money
    • We update answer with max(ans, s + b)
  • 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)

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 Evaluator

Example 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:

  1. 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 in s=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
  2. If [2, 1] is last (losing transaction):

    • The loss from [2, 1] is already in s=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
  3. 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

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: need 5 + 5 = 10
  • Checking [100, 101] last: need 5 + 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.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!

Load More