The best time to buy and sell stocks includes handling fees

Posted by eRott on Thu, 16 Dec 2021 07:01:04 +0100

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;
};