Say you have an array for which the i-th element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most k transactions.
Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
Example 1:
Input: [2,4,1], k = 2 Output: 2 Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2.
Example 2:
Input: [3,2,6,5,0,3], k = 2 Output: 7 Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4. Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
The core challenge of this problem is to maximize the profit from at most k transactions. Each transaction consists of buying and then selling the stock. The constraint is that you cannot engage in multiple transactions simultaneously, meaning you must sell the stock before you can buy again.
This problem is significant in financial markets where traders aim to maximize their returns by strategically buying and selling stocks. A common pitfall is to assume that more transactions always lead to higher profits, which is not necessarily true due to the constraints.
To solve this problem, we need to consider dynamic programming due to the overlapping subproblems and optimal substructure properties.
Let's start with a naive solution:
This approach is not optimal because it has a time complexity of O(n^k), which is infeasible for large inputs.
We can use dynamic programming to optimize this. The idea is to maintain two arrays:
We update these arrays iteratively to build up the solution.
1. Initialize a 2D array dp with dimensions (n+1) x (k+1) to store the maximum profit.
2. Iterate over the number of transactions from 1 to k.
3. For each transaction, iterate over the days from 1 to n.
4. Update the maxDiff and dp arrays based on the current price and previous profits.
/**
* @param {number[]} prices
* @param {number} k
* @return {number}
*/
function maxProfit(prices, k) {
const n = prices.length;
if (n == 0) return 0;
// If k is greater than n/2, it's equivalent to unlimited transactions
if (k > n / 2) {
let maxProfit = 0;
for (let i = 1; i < n; i++) {
if (prices[i] > prices[i - 1]) {
maxProfit += prices[i] - prices[i - 1];
}
}
return maxProfit;
}
// Initialize dp array
const dp = Array.from({ length: k + 1 }, () => Array(n).fill(0));
for (let t = 1; t <= k; t++) {
let maxDiff = -prices[0];
for (let d = 1; d < n; d++) {
dp[t][d] = Math.max(dp[t][d - 1], prices[d] + maxDiff);
maxDiff = Math.max(maxDiff, dp[t - 1][d] - prices[d]);
}
}
return dp[k][n - 1];
}
// Example usage:
console.log(maxProfit([2, 4, 1], 2)); // Output: 2
console.log(maxProfit([3, 2, 6, 5, 0, 3], 2)); // Output: 7
The time complexity of this solution is O(n*k) because we have two nested loops iterating over the days and the number of transactions. The space complexity is also O(n*k) due to the 2D dp array.
Consider the following edge cases:
To test the solution comprehensively, consider the following test cases:
Use a testing framework like Jest or Mocha to automate the testing process.
When approaching such problems, consider the following tips:
Practice solving similar problems to improve your problem-solving skills.
In this blog post, we discussed the problem of finding the maximum profit from at most k transactions. We explored a dynamic programming approach to solve the problem efficiently. Understanding and solving such problems is crucial for developing strong algorithmic skills.
For further reading and practice, consider the following resources: