2412. Minimum Money Required Before Transactions
Problem Description
In this problem, we're given an array transactions
that represents a series of transactions. Each transaction is described by two values: a cost
and a cashback
. The cost
is the amount of money that must be paid to perform the transaction, and the cashback
is the amount that is returned after the transaction is completed. Our goal is to find the minimum amount of money (money
) required before any transaction is made so that it's possible to complete all transactions in any order. The key condition is that at any point, money
must be greater than or equal to the cost
of the transaction being performed, and after each transaction, money
updates to money - cost + cashback
.
Intuition
Coming up with a solution involves understanding that there are certain transactions that are riskier in terms of running out of money. Specifically, transactions where cost
is greater than cashback
are risky because they deplete the pool of money more than any other transaction types. Therefore, one needs to ensure they have enough money to handle the worst-case scenario for these transactions.
Here's the intuition to solve the problem:
-
Calculate the minimum amount of money needed to ensure that you always have enough to cover the
cost
for the riskier transactions. This is done by finding the sum of the differences betweencost
andcashback
for these transactions (sum(max(0, a - b) for a, b in transactions)
). -
Keep track of the maximum additional money required on top of this sum, which comes from the
cashback
(orcost
ifcashback
is greater) of the transactions. This step includes the consideration for the transaction with the highestcashback
that would minimize the amount of money needed upfront.
The solution iterates through each transaction to calculate these values. The initial sum represents the minimum starting money required to perform all riskier transactions one after another, without the cashback from any transaction dichotomizing the sum. The 'ans' being the maximum helps to keep a running maximum of what that minimum starting money might be considering the cashbacks (or cost, whichever is lower for each of the less risky transactions).
By the end of the iteration, if a transaction's cost
is greater than its cashback
, the maximum of the sum plus cashback
becomes a potential answer. Otherwise, the maximum of the sum plus cost
is considered for the less risky transactions. The final ans
value is the minimum amount of money required to start with to complete all transactions in any order.
Solution Approach
The solution uses a simple linear scan algorithm, which is efficient since it only needs to iterate through the transactions once. It performs two key calculations:
-
It first calculates the sum of all the positive differences between
cost
andcashback
for each transaction. This is because, for transactions wherecost
is greater thancashback
, you will lose some money, and you need to have enough in reserve to cover these losses. The calculation is done with a generator expression in Python:s = sum(max(0, a - b) for a, b in transactions)
Here,
max(0, a - b)
ensures that we only sum positive differences, as we do not need extra money upfront for transactions wherecashback
is greater or equal tocost
. -
The code then determines the maximum additional money required to ensure that transactions can be completed in any order. It iterates through the transactions, comparing each
cost
andcashback
:ans = 0 for a, b in transactions: if a > b: ans = max(ans, s + b) else: ans = max(ans, s + a)
For riskier transactions (
a > b
), the code considers the possibility of doing this transaction first when the reserves
is at its full. After payinga
(cost), you receiveb
(cashback), so you need at leasts + b
to start with to do this transaction without going broke.For transactions that are not riskier (
a <= b
), because they refund equal or more than their cost, doing them first would only require enough money to cover the cost, which isa
. Thus, for each transaction, you check ifs + a
(cost) is a new maximum requirement to start with.
The final result in ans
is the minimum money required before any transaction that allows you to complete all of them regardless of order. The solution is efficient because it has a time complexity of O(n), where n is the number of transactions, and does not require any additional complex data structures, making use of only iteration and comparison to arrive at the solution.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to better understand the solution approach. Suppose we have the following transactions
:
- Transaction 1:
cost
= 5,cashback
= 2 - Transaction 2:
cost
= 3,cashback
= 3 - Transaction 3:
cost
= 6,cashback
= 1
We need to find the minimum amount of money (money
) required to complete all these transactions in any order.
First, we calculate the sum of all the positive differences between cost
and cashback
. In our example:
- For Transaction 1:
cost
-cashback
= 5 - 2 = 3 (positive difference) - For Transaction 2:
cost
-cashback
= 3 - 3 = 0 (no positive difference) - For Transaction 3:
cost
-cashback
= 6 - 1 = 5 (positive difference)
We sum these up to get the total sum s
:
sum = 3 (from Transaction 1) + 0 (from Transaction 2) + 5 (from Transaction 3) = 8
Next, we iterate over each transaction to decide the maximum additional money required. We start with ans = 0
:
- Transaction 1: Since
cost
>cashback
, we considers + cashback
which is8 + 2 = 10
. We updateans
to 10 as it's greater than the currentans
(0). - Transaction 2: Since
cost
<=cashback
, we considers + cost
which is8 + 3 = 11
. We updateans
to 11 as it's greater than the currentans
(10). - Transaction 3: Since
cost
>cashback
, we considers + cashback
which is8 + 1 = 9
.ans
remains 11 as it's greater than 9.
After considering each transaction, the final answer ans
is 11. This is the minimum amount of money we need to have upfront before starting any transactions to be able to complete all of them in any order.
Solution Implementation
1from typing import List
2
3class Solution:
4 def minimumMoney(self, transactions: List[List[int]]) -> int:
5 # Calculate the initial sum required to cover all negative cash flows,
6 # i.e., where the cost of a transaction is higher than the cashback
7 total_negative_cash_flow = sum(max(0, cost - cashback) for cost, cashback in transactions)
8
9 # Initialize maximum additional money required at the start as zero
10 max_additional_money_required = 0
11
12 # Iterate through all transactions to determine the maximum additional money
13 # required at the start to not end up with a negative balance at any point
14 for cost, cashback in transactions:
15 if cost > cashback:
16 # If transaction cost is higher than cashback, calculate additional money
17 # required by considering the total negative cash flow and cashback of
18 # the current transaction
19 max_additional_money_required = max(max_additional_money_required,
20 total_negative_cash_flow + cashback)
21 else:
22 # If transaction cost is lower or equal to cashback,
23 # calculate by considering total negative cash flow and cost of
24 # the current transaction
25 max_additional_money_required = max(max_additional_money_required,
26 total_negative_cash_flow + cost)
27
28 # Return the maximum additional money that is required at the beginning
29 return max_additional_money_required
30
1class Solution {
2 public long minimumMoney(int[][] transactions) {
3 // Initialize a sum variable to hold the total amount of initial money we need
4 // without considering cashback.
5 long totalWithoutCashback = 0;
6
7 // Iterate over each transaction.
8 for (int[] transaction : transactions) {
9 // To not go negative at the start of a transaction, we need to have enough money
10 // that even if we don't get the cashback immediately, we won't go bankrupt.
11 // Hence, we add the net loss of each transaction to the initial money required.
12 totalWithoutCashback += Math.max(0, transaction[0] - transaction[1]);
13 }
14
15 // Initialize a variable to store the answer, which is the final minimum money needed.
16 long minMoneyRequired = 0;
17
18 // Iterate over each transaction again to find the peak money needed at any transaction.
19 for (int[] transaction : transactions) {
20 if (transaction[0] > transaction[1]) {
21 // If the cost is greater than the cashback, the peak money will be the total
22 // without considering cashback plus the cashback of this transaction.
23 // This ensures we have enough during the transaction and after getting the cashback.
24 minMoneyRequired = Math.max(minMoneyRequired, totalWithoutCashback + transaction[1]);
25 } else {
26 // If the cashback is greater than or equal to the cost, we just need the cost of
27 // this transaction as the peak money, on top of the initially calculated money,
28 // because we'll get back as much or more than we spend.
29 minMoneyRequired = Math.max(minMoneyRequired, totalWithoutCashback + transaction[0]);
30 }
31 }
32
33 // Return the maximum of money we need at any point which will be enough to start all transactions.
34 return minMoneyRequired;
35 }
36}
37
1class Solution {
2public:
3 long long minimumMoney(vector<vector<int>>& transactions) {
4 // Initialize the sum of costs and the answer which will store the minimum initial money required
5 long long costSum = 0, minInitialMoney = 0;
6
7 // Calculate the sum of extra costs needed after transactions that cost more than you get back
8 for (auto& transaction : transactions) {
9 costSum += max(0, transaction[0] - transaction[1]);
10 }
11
12 // Determine the minimum initial money needed before any of the transactions
13 for (auto& transaction : transactions) {
14 if (transaction[0] > transaction[1]) {
15 // For transactions that cost more than you get back, add back the money obtained from that transaction
16 minInitialMoney = max(minInitialMoney, costSum + transaction[1]);
17 } else {
18 // For transactions that cost less or equal to what you get back, take the maximum of the cost
19 minInitialMoney = max(minInitialMoney, costSum + transaction[0]);
20 }
21 }
22
23 // Return the calculated minimum initial money required to complete all transactions
24 return minInitialMoney;
25 }
26};
27
1function minimumMoney(transactions: number[][]): number {
2 // Initialize the sum of costs and the answer, which will store the minimum initial money required
3 let costSum: number = 0;
4 let minInitialMoney: number = 0;
5
6 // Calculate the sum of extra costs needed after transactions that cost more than you get back
7 for (const transaction of transactions) {
8 costSum += Math.max(0, transaction[0] - transaction[1]);
9 }
10
11 // Determine the minimum initial money needed before any of the transactions
12 for (const transaction of transactions) {
13 if (transaction[0] > transaction[1]) {
14 // For transactions that cost more than what you get back, add back the money obtained from that transaction
15 minInitialMoney = Math.max(minInitialMoney, costSum + transaction[1]);
16 } else {
17 // For transactions that cost less than or equal to what you get back, take the maximum of the cost
18 minInitialMoney = Math.max(minInitialMoney, costSum + transaction[0]);
19 }
20 }
21
22 // Return the calculated minimum initial money required to complete all transactions
23 return minInitialMoney;
24}
25
Time and Space Complexity
Time Complexity
The given code consists of two main parts which contribute to the time complexity:
-
The first part is the sum operation with a generator expression:
s = sum(max(0, a - b) for a, b in transactions)
For each transaction in the list
transactions
, it calculates the maximum between0
anda - b
. This operation has a time complexity of O(N), where N is the number of transactions. -
The second part is a loop that iterates over all transactions again to determine the maximum money required:
for a, b in transactions: if a > b: ans = max(ans, s + b) else: ans = max(ans, s + a)
This loop runs for each transaction, making it also O(N).
Since these two parts run sequentially, the overall time complexity is the sum of both parts, which is O(N) + O(N), resulting in a total time complexity of O(N).
Space Complexity
The space complexity of the code is O(1), disregarding the input size. This is because the space used by the variables s
and ans
is constant and does not depend on the size of the input list transactions
. The generator expression also does not create an intermediate list, which keeps the space complexity low.
Learn more about how to find time and space complexity quickly using problem constraints.
What does the following code do?
1def f(arr1, arr2):
2 i, j = 0, 0
3 new_arr = []
4 while i < len(arr1) and j < len(arr2):
5 if arr1[i] < arr2[j]:
6 new_arr.append(arr1[i])
7 i += 1
8 else:
9 new_arr.append(arr2[j])
10 j += 1
11 new_arr.extend(arr1[i:])
12 new_arr.extend(arr2[j:])
13 return new_arr
14
1public static List<Integer> f(int[] arr1, int[] arr2) {
2 int i = 0, j = 0;
3 List<Integer> newArr = new ArrayList<>();
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.add(arr1[i]);
8 i++;
9 } else {
10 newArr.add(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.add(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.add(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
1function f(arr1, arr2) {
2 let i = 0, j = 0;
3 let newArr = [];
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.push(arr1[i]);
8 i++;
9 } else {
10 newArr.push(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.push(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.push(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
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
LeetCode 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 we
Want a Structured Path to Master System Design Too? Don’t Miss This!