714. The best time to buy and sell stocks includes handling fees
Link to force buckle topic: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/ )
Given an integer array prices, where the i-th element represents the stock price on the i-th day; The nonnegative integer fee represents the handling fee for trading stocks.
You can complete transactions indefinitely, but you need to pay a handling fee for each transaction. If you have bought a stock, you can't continue to buy it until you sell it.
Returns the maximum profit.
Note: a transaction here refers to the whole process of buying, holding and selling stocks. You only need to pay a handling fee for each transaction.
Example 1:
- Input: prices = [1, 3, 2, 8, 4, 9], fee = 2
- Output: 8
Explanation: maximum profit that can be achieved:
- Buy here prices[0] = 1
- Sell here prices[3] = 8
- Buy here prices[4] = 4
- Sell here prices[5] = 9
- Total profit: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
be careful:
- 0 < prices.length <= 50000.
- 0 < prices[i] < 50000.
- 0 <= fee < 50000.
thinking
This question is relative to Greedy algorithm: 122 The best time to buy and sell stocks II , an additional condition is the handling fee.
Greedy Algorithm
stay Greedy algorithm: 122 The best time to buy and sell stocks II When using the greedy strategy, you don't have to care about the specific time of trading. As long as you collect the positive profits every day, the final stable is the maximum profit.
If there is a handling fee in this topic, it will be related to when to buy and sell, because to calculate the profit obtained, we need to consider that the trading profit may not be enough for the handling fee.
If the greedy strategy is used, it is to buy at the lowest value and sell at the highest value (if the Commission is included and the profit is made).
At this point, we just need to find two points, the buy date and the sell date.
- Buying date: actually, it's good to think about it. Record it when you encounter a lower point.
- Selling date: This is not easy to calculate, but it is not necessary to calculate the accurate selling date. As long as the current price is greater than (minimum price + handling fee), you can harvest profits. As for the accurate selling date, it is the last day in the continuous profit range (it is not necessary to calculate the specific day).
Therefore, there are actually three situations when we harvest profits:
- Situation 1: the day of profit harvest is not the last day in the profit range (not really selling, equivalent to holding stocks), so we should continue to harvest profits later.
- Situation 2: the previous day was the last day in the profit range (equivalent to the real sale), and the minimum price should be recorded again today.
- Case 3: do not operate and keep the original state (buy, sell, do not buy, do not sell)
Greedy algorithm C + + code is as follows:
class Solution { public: int maxProfit(vector<int>& prices, int fee) { int result = 0; int minPrice = prices[0]; // Record lowest price for (int i = 1; i < prices.size(); i++) { // Case 2: equivalent to buying if (prices[i] < minPrice) minPrice = prices[i]; // Situation 3: keep the original state (because buying is not cheap at this time, and selling is at a loss) if (prices[i] >= minPrice && prices[i] <= minPrice + fee) { continue; } // To calculate the profit, there may be many times to calculate the profit, and the last time to calculate the profit is the real meaning of selling if (prices[i] > minPrice + fee) { result += prices[i] - minPrice - fee; minPrice = prices[i] - fee; // Situation one, this step is critical } } return result; } };
- Time complexity: O(n)
- Space complexity: O(1)
From the code, we can see the operation of case 1. If it is still in the profit range, it means that it is not a real sale, and the handling fee must be deducted every time to calculate the profit, so minPrice = prices[i] - fee;, In this way, when the profit is harvested tomorrow, the handling fee will not be reduced again!
You can also find that in case 3, the code can be deleted. I want to make the code clear, so I don't simplify it.
dynamic programming
This problem solution first gives my C + + code (with detailed comments). Interested students can learn it by themselves.
be relative to Greedy algorithm: 122 The best time to buy and sell stocks II In the dynamic programming solution of, you only need to subtract the handling fee when calculating the selling operation, and the code is almost the same.
The C + + code is as follows:
class Solution { public: int maxProfit(vector<int>& prices, int fee) { // dp[i][1] maximum cash held on day I // dp[i][0] maximum cash left over from holding shares on day I int n = prices.size(); vector<vector<int>> dp(n, vector<int>(2, 0)); dp[0][0] -= prices[0]; // Holding shares for (int i = 1; i < n; i++) { dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee); } return max(dp[n - 1][0], dp[n - 1][1]); } };
- Time complexity: O(n)
- Space complexity: O(n)
Of course, the space can be optimized, because the current state only depends on the previous state.
The C + + code is as follows:
class Solution { public: int maxProfit(vector<int>& prices, int fee) { int n = prices.size(); int holdStock = (-1) * prices[0]; // Holding shares int saleStock = 0; // Sell stock for (int i = 1; i < n; i++) { int previousHoldStock = holdStock; holdStock = max(holdStock, saleStock - prices[i]); saleStock = max(saleStock, previousHoldStock + prices[i] - fee); } return saleStock; } };
- Time complexity: O(n)
- Space complexity: O(1)
summary
The greedy idea of this topic is actually more difficult. Dynamic planning is the conventional practice, but it can also be regarded as expanding your ideas and feeling the charm of greed.
Later, when we explain the series of stock problems, we will thread the stock problems in the way of dynamic rules.
Other language versions
Java:
// Greedy thinking class Solution { public int maxProfit(int[] prices, int fee) { int buy = prices[0] + fee; int sum = 0; for (int p : prices) { if (p + fee < buy) { buy = p + fee; } else if (p > buy){ sum += p - buy; buy = p; } } return sum; } }
class Solution { // dynamic programming public int maxProfit(int[] prices, int fee) { if (prices == null || prices.length < 2) { return 0; } int[][] dp = new int[prices.length][2]; // bad case dp[0][0] = 0; dp[0][1] = -prices[0]; for (int i = 1; i < prices.length; i++) { dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee); dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]); } return dp[prices.length - 1][0]; } }
Python
class Solution: # Greedy thinking def maxProfit(self, prices: List[int], fee: int) -> int: result = 0 minPrice = prices[0] for i in range(1, len(prices)): if prices[i] < minPrice: minPrice = prices[i] elif prices[i] >= minPrice and prices[i] <= minPrice + fee: continue else: result += prices[i] - minPrice - fee minPrice = prices[i] - fee return result
Go
func maxProfit(prices []int, fee int) int { var minBuy int = prices[0] //First day buy var res int for i:=0;i<len(prices);i++{ //If the current price is less than the lowest price, buy here if prices[i]<minBuy{ minBuy=prices[i] } //If you sell at a loss at the current price, don't sell and continue to find the next selling point if prices[i]>=minBuy&&prices[i]-fee-minBuy<=0{ continue } //It's ready for sale if prices[i]>minBuy+fee{ //Accumulate daily earnings res+=prices[i]-minBuy-fee //Update the minimum value (if it is still in the profit range, it means that it is not a real sale, and the handling fee will be deducted every time when calculating the profit, so make minBuy = prices[i] - fee; so that the handling fee will not be reduced again when the profit is harvested tomorrow!) minBuy=prices[i]-fee } } return res }
Javascript
// Greedy thinking var maxProfit = function(prices, fee) { let result = 0 let minPrice = prices[0] for(let i = 1; i < prices.length; i++) { if(prices[i] < minPrice) { minPrice = prices[i] } if(prices[i] >= minPrice && prices[i] <= minPrice + fee) { continue } if(prices[i] > minPrice + fee) { result += prices[i] - minPrice - fee // Buying and selling only need to pay a handling fee minPrice = prices[i] -fee } } return result }; // dynamic programming /** * @param {number[]} prices * @param {number} fee * @return {number} */ var maxProfit = function(prices, fee) { // Scroll array // have represents the maximum return on shares held that day // notHave represents the maximum return of not holding shares on the day // Include the handling charge in the purchase price let n = prices.length, have = -prices[0]-fee, // Maximum return on holding shares on day 0 notHave = 0; // Maximum return from not holding shares on day 0 for (let i = 1; i < n; i++) { // The maximum return on holding shares on day i consists of two cases // 1. i already held shares on day i-1 and did nothing on day i // 2. Do not hold stocks on day i-1, just buy on day i have = Math.max(have, notHave - prices[i] - fee); // The maximum return of not holding shares on day i consists of two cases // 1. i didn't hold stocks on day i-1, and i didn't do anything on day i // 2. Stocks held on day i-1 and just sold on day i notHave = Math.max(notHave, have + prices[i]); } // Finally, if you don't hold stocks, you can maximize your income return notHave; };