代码随想录Day54

今天继续学习动规解决相关问题,昨天因为身体状态出了点问题因此昨天的分析较为简陋,今天已经更改。

188.买卖股票的最佳时机IV

给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
示例 2:

输入:k = 2, prices = [3,2,6,5,0,3]
输出:7
解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
     随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。

思路:

1.有了前面的基础,再加上本题是至多k次买卖,显然我们需要从前面的一次买卖、两次买卖中抽象出一条规律。可以发现每一次买卖对应两个状态——第j次持有和第j次不持有,因此k次买卖我们就应当有2k个状态,因此dp数组的第二个维度容量应当初始化为2k + 1(涉及到0,即不操作)。并且初始化的时候我们需要把第0天所有的持有状态给初始化为-prices[0]。

2.然后就是寻找持有状态和不持有状态之间的通性,这一点其实我已经在Day53中说明过了:持有状态要么是当天买入,要么是延续前一天的已经持有;非持有状态要么是当天卖出,要么是延续前一天的非持有。明确以上后我们就可以抽象出规律然后写出如下代码了:

class Solution {
public:
    int maxProfit(int k, vector& prices) {
        vector> dp(prices.size(), vector(2 * k + 1, 0));

        //初始化第0天的所有持有状态
        for(int i = 1; i < k * 2; i += 2){
            dp[0][i] = - prices[0];
        }

        for(int i = 1; i < prices.size(); i++){
            for(int j = 0; j < k * 2; j += 2){
                //初始化第i天的第j + 1次持有状态
                dp[i][j + 1] = max(dp[i - 1][j + 1],dp[i - 1][j] - prices[i]);
                //初始化第i天的第j + 1次未持有状态
                dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
            }
        }

        return dp[prices.size() - 1][2 * k];
    }
};

启发:

1.本题有了前面部分的基础后能够很轻松想到思路,但在写递推公式部分的时候难免会混淆第i天第j+1次持有和第j+1次非持有的各项参数,此时如果实在想不清楚建议代入几个具体数字进行举例,再联想前面已经做过的限制一次买卖、两次买卖等。

309.最佳买卖股票时机含冷冻期

给定一个整数数组prices,其中第  prices[i] 表示第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: prices = [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
示例 2:

输入: prices = [1]
输出: 0

思路:

1.本题是不限制买卖次数,相当于在||的基础上增加了一个冷冻期的设定。冷冻期的出现与卖出股票这一状态息息相关,如果我们依旧按照原本的持有股票与非持有股票进行状态划分很明显是无法区分出冷冻期这一状态的,因此我们需要把非持有股票状态给拆开,拆成当天卖出股票状态和保持卖出股票后状态,在此基础上我们再加入冷冻期状态。

2.首先想dp数组含义,经过1的分析我们可以将状态划分为4种,因此dp数组的第二个维度容量就为4,dp[i][0]表示持有股票状态,dp[i][1]表示保持卖出股票后的状态,dp[i][2]表示当天卖出股票,dp[i][3]表示冷冻期

3.然后想递推公式,对于持有股票状态,可以是延续前一天的延续股票(dp[i - 1][0]),可以是前一天是保持的卖出股票后状态(dp[i - 1][1] - prices[i]),也可以是前一天是冷冻期(dp[i - 1][3] - prices[i]),对于以上三种状态取最大值。

对于保持卖出股票状态,可以是延续前一天的卖出股票状态(dp[i - 1][1]),也可以是前一天是冷冻期(dp[i - 1][3]),对以上两种状态取最大值

对于卖出股票状态,只可能是前一天处于持有股票状态,即dp[i - 1][0] + prices[i]。

对于冷冻期,只可能是前一天处于卖出股票状态,即dp[i - 1][2]。

4.然后想初始化,由递推公式我们可以看出所有后续状态都由dp[0][j]推出,对于第0天,如果持有那么显然就是第0天买入,即dp[0][0] = - prices[0];如果卖出那么dp[0][2] = 0(买入后立马卖出);如果处于保持卖出状态理论上来说就是前一种情况,因此dp[0][1] = 0,;如果处于冷冻期,理论上来说冷冻期不可能出现在第一天,是一种非法状态,不过由递推公式我们可以看出,要用到dp[0][3]的情况只会在第1天持有股票或者第1天保持卖出股票,为了保证结果正确我们也将其初始化为0。

5.最后是遍历顺序,本题显然是从前往后遍历。

class Solution {
public:
    int maxProfit(vector& prices) {
        vector> dp(prices.size(), vector(4,0));

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

        for(int i = 1; i < prices.size(); i++){
            //持有状态
            dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][1] - prices[i], dp[i - 1][3] - prices[i]));
            //保持卖出后状态
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
            //当天卖出股票
            dp[i][2] = dp[i -1][0] + prices[i];
            //冷冻期
            dp[i][3] = dp[i - 1][2];
        }

        return max(dp[prices.size() - 1][3], max(dp[prices.size() - 1][1], dp[prices.size() - 1][2]));
    }
};

启发:

1.有了前面这么多最佳买卖股票时机的积累后,逐渐发现确定状态和状态转移是解决这类题的关键所在,需要根据具体题目确定好所有的状态然后在根据状态转移得到递推公式。

你可能感兴趣的:(代码随想录,算法,动态规划,数据结构,leetcode,c++)