xiedaxia1hao / leetcode_pattern

1 stars 0 forks source link

Stock #24

Open xiedaxia1hao opened 3 years ago

xiedaxia1hao commented 3 years ago

所有股票类型的算法题总结
母题188搞明白了,其他的题目换汤不换药

Shout out to this post and video for brilliant discussion!

xiedaxia1hao commented 3 years ago

188. Best Time to Buy and Sell Stock IV

You are given an integer array prices where prices[i] is the price of a given stock on the ith day.

Design an algorithm to find the maximum profit. You may complete at most k transactions.

Notice that you may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).


写代码前先理清楚本题的三个状态,天数是否持有股票的状态交易次数

而对于每种状态,我们又有不同的选择:

  1. 天数由prices确定
  2. 是否持有股票的状态我们可以用0/1表示,0代表不持有股票1代表持有股票
  3. 交易次数由k确定

这样我们可以定一个三维dp[i][k][j]数组,其存储的内容是在第i天,我们持有股票的状态为j,已经进行了k次交易时能够获取的最大利润。

这里需要说明的是,第i天和数组的索引一致,它的范围是0 ~ prices.length-1,所以我设置的起点是第0天

比如说dp[5][0][2]:其中存储了 「在第5天时,我们不持有股票,已经进行了2次交易」 时获取的最大利润。

好了,到这里dp的定义和状态我们就确定下来了,那么初始值怎么确定呢?

因为对于第0天来说,它所能够获取的最大利润只有两种,与交易次数毫无关系,所以我们有以下初始值: 1.不持有股票时:dp[0][k][0]=0 2.持有股票时:dp[0][k][1]=-prices[0]

好了,到这里初始值也确定下来了,那么还剩下最后一个问题「状态方程」

我们首先要知道一点,第i天的状态只和第i-1天有关,第i天持有/不持有股票 能够获取的最大利润取决于 第i-1天持有/不持有股票 这里需要注意:我将买入股票时作为一次交易,也就是在买入股票的时候交易次数+1

第i天不持有股票时: 1.如果第i-1天也不持有股票,那就代表状态不变,第i天的状态=第i-1天的状态 2.如果第i-1天持有股票,说明我们在第i天把股票卖了,既然卖股票赚钱了,利润就要多prices[i] dp[i][k][0] = max(dp[i-1][k][0] , dp[i-1][k][1]+prices[i])

第i天持有股票时: 1.如果第i-1天也持有股票,那就代表状态不变,即dp[i][k][1] = dp[i-1][k][1] 2.如果第i-1天不持有股票,说明我们在第i天买入股票,既然买股票要花钱,利润就要少price[i] 买入股票的同时,当天的交易次数要在前一天的基础上+1 dp[i][k][1] = max(dp[i-1][k][1] , dp[i-1][k-1][0]-prices[i])

还需要注意的点:因为一次交易涉及一天买入一天卖出,一共两天,所以如果k值大于数组长度的一半,k就直接取数组长度的一半,因为多余的交易次数无法达到 即:k=min(k , prices.length/2)

class Solution {
    public int maxProfit(int K, int[] prices) {

        // Edge Case
        if(prices == null || prices.length < 1) {
            return 0;
        }

        int n = prices.length;
        K = Math.min(K, n/2);
        int[][][] dp = new int[n][K+1][2];

        for(int k = 0; k <= K; k++) {
            dp[0][k][0] = 0;
            dp[0][k][1] = -prices[0];
        }

        // NOTE: the stating point of i and k has to be 1, as we will need to use i-1 and k-1 inside our loop
        for(int i = 1; i < n; i++) {
            for(int k = 1; k <= K; k++) {
                dp[i][k][0] = Math.max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]);
                dp[i][k][1] = Math.max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]);
            }
        }

        return dp[n-1][K][0];

    }
}
xiedaxia1hao commented 3 years ago

188题解决了以后,其实后面的几道题目都迎刃而解了。

123. Best Time to Buy and Sell Stock III

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

这题其实就是上面一题的轻微变式,let k = 2即可。

122. Best Time to Buy and Sell Stock II

You are given an array prices for which the ith element is the price of a given stock on day i.

Find the maximum profit you can achieve. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times).

Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).

这题不限制交易次数。那我们可以通过建立一个2d dp table来实现。

class Solution {
    public int maxProfit(int[] prices) {

        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][0] = 0; 
        dp[0][1] = -prices[0];

        for(int i = 1; i < n; i++) {
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i]);
        }

        return dp[n-1][0];
    }
}

121. Best Time to Buy and Sell Stock

You are given an array prices where prices[i] is the price of a given stock on the ith day.

You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock.

Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.

该题限制了交易次数k为1。

class Solution {
    public int maxProfit(int[] prices) {

        int n = prices.length;
        int[][] dp = new int [n][2];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];

        for(int i = 1; i < n; i++) {
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i-1][1], -prices[i]); // 因为限定交易次数,所以要进行交易的时候要注意
        }

        return dp[n-1][0];
    }
}

714. Best Time to Buy and Sell Stock with Transaction Fee

Your are given an array of integers prices, for which the i-th element is the price of a given stock on day i; and a non-negative integer fee representing a transaction fee.

You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction. You may not buy more than 1 share of a stock at a time (ie. you must sell the stock share before you buy again.)

Return the maximum profit you can make.

每次卖出股票的时候,支付手续费即可。

class Solution {
    public int maxProfit(int[] prices, int fee) {

        if(prices == null || prices.length < 1) {
            return 0;
        }

        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];

        for(int i = 1; i < n; 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[n-1][0];
    }
}

309. Best Time to Buy and Sell Stock with Cooldown

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:

You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)

冷冻期就是:卖出股票后,你无法在第二天买入股票。

那这题需要做一些tweak。我们要在这个时候保存3个状态:

直接设置三个持有状态: dp[i][0]:持有股票 dp[i][1]:不持有股票,处于冷冻期 dp[i][2]:不持有股票,不处于冷冻期

dp[i][0]=max(dp[i-1][0] , dp[i-1][2]-prices[i]) //当天持有股票,前一天不可能是冷冻期,也就没有dp[i-1][1] dp[i][1]=dp[i-1][0]+prices[i] //当天是冷冻期,只可能是前一天持有股票,然后今天卖出股票了 dp[i][2]=max(dp[i-1][1],dp[i-1][2]) //当天是非冷冻期,前一天不可能持有股票

class Solution {
    public int maxProfit(int[] prices) {

        if(prices == null || prices.length < 1) {
            return 0;
        }

        int n = prices.length;
        int[][] dp = new int[n][3];

        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        dp[0][2] = 0;

        for(int i = 1; i < n; i++) {
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][2] - prices[i]);
            dp[i][1] = dp[i-1][0] + prices[i];
            dp[i][2] = Math.max(dp[i-1][1], dp[i-1][2]);
        }

        return Math.max(dp[n-1][1], dp[n-1][2]); // 返回不持有股票状态的最大值
    }
}