代码随想录算法训练营第五十一天|309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费

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

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

思路:

本题思路和买卖股票的最佳时机 II一致,都是可以多次购买,累计所获得的金额。稍有不同的地方是,本题增加了一个冷冻期,因此在dp数组中需要增加一个状态来表示冷冻期时所持有的最大金额,并且在递推公式中也和冷冻期有关。

首先定义下标:在第i天,dp[i][[0]表示持有股票的状态下所能获得的最大金额,dp[i][1]表示未持有股票,并且不处于冷冻期的状态下所能获得的最大金额,dp[i][2]表示未持有股票,并且处于冷冻其所能获得的最大金额。然后初始化dp,dp[0][0]表示第一天购买股票,所以dp[0][0] = -prices[0],其他两个状态持有最大金额均为0。

接下来是递推公式,三种状态各需要一个递推公式。

  1. dp[i][2] = dp[i-1][1]。第i天为冷冻期所持有的最大金额,当为冷冻期的时候,既不能买入也不能卖出,因为冷冻期的时间只有一天,所以冷冻期前一天一定是出售股票了的,所持有金额应和前一天未持有股票金额相当,即dp[i-1][1]。
  2. dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]). 第i天未持有股票且不为冷冻期,最大持有金额可以由以下两种情况得到。1. 前一天就未持有股票,金额为dp[i-1][1]。2. 前一天持有股票,当天出售,持有金额为前一天持有股票的金额dp[i-1][0],加上当天出售股票获得的价格prices[i]。
  3. dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i])。第i天持有股票所获得的最大金额可以由以下两种情况得到:1. 前一天就持有股票,所持有金额为dp[i-1][0]。2. 前一天未持有股票,所持有金额为前一天未持有股票时的金额减去当天股票的价格,那么前一天未持有的股票金额是选取dp[i-1][[1]还是dp[i-1][2]呢,它们都是未持有股票的情况,前者为不处于冷冻期状态下,后者为处于冷冻期状态下。不妨把两种情况都考虑一遍:
    1. 如果前一天为冷冻期,说明我们在前前一天,也就是i-2天,出售了股票,这个时候只能选取dp[i-1][2]而不能选取dp[i-1][1],因为dp[i-1][1]表明我们没有在i-2天出售股票,dp[i-1][1]的值和dp[i-1][2]的值不相同。
    2. 如果前一天不为冷冻期,说明我们至少在前前一天,也就是i-2天是没有出售股票的,这个时候dp[i-1][1]的值和dp[i-1][2]的值相同,因为根据第一条递推公式,dp[i-1][2]沿用了前一天的值dp[i-2][1],并且我们知道,前前一天是没有出售股票的,所以前一天未持有股票时的金额dp[i-1][1],应该和前前一天未持有股票时的金额dp[i-2][1]相等,此时dp[i-1][1] = dp[i-2][2]。所以还是可以选取dp[i-2][2]作为前一天未持有股票时的最大金额。

递推公式就是以上三条,然后从i=1遍历prices数组,直到最后一天,因为我们知道未持有股票时的最大金额一定是大于持有股票的,所以我们需要在未持有股票时的两种状态,也就是冷冻期和非冷冻期之间选一个较大的金额作为最终结果,也就是max(dp[prices.size()-1][1], dp[prices.size()-1][2])。

代码:

class Solution {
public:
    int maxProfit(vector& prices) {
        if (prices.size() <= 1)
            return 0;
        vector> dp(prices.size(), vector(3));
        // 0:持有 1:未持有 2:冷冻期
        dp[0][0] = -prices[0];
        for (int i = 1; i < prices.size(); i++)
        {
            dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i]);
            dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]);
            dp[i][2] = dp[i-1][1];
        }
        return max(dp[prices.size()-1][1], dp[prices.size()-1][2]);
    }
};

LeetCode 714.买卖股票的最佳时机含手续费

链接:714.买卖股票的最佳时机含手续费

思路:

本题和122. 买卖股票的最佳时机 II几乎是一模一样了,除了增加了一个手续费以外,没有冷冻期,也不限制买入次数,所以做法几乎是一样的,递推公式唯一的区别就在,计算未持有股票的价格的时候,卖出股票时加上当天的股票价格prices[i]后,要减掉手续费fee,其他部分代码完全一样。

虽然在没有手续费的时候,这种方法看起来很麻烦,看似不如用贪心法,但是一旦题目变得更加复杂,贪心法的策略也更加难写,但是用动态规划的话就很容易解决了,思路也比较简单。

代码:

class Solution {
public:
    int maxProfit(vector& prices, int fee) {
        vector> dp(2, vector(2));
        dp[0][0] = -prices[0];
        for (int i = 1; i < prices.size(); i++)
        {
            dp[i % 2][0] = max(dp[(i-1) % 2][0], dp[(i-1) % 2][1] - prices[i]);
            dp[i % 2][1] = max(dp[(i-1) % 2][1], dp[(i-1) % 2][0] + prices[i] - fee);
        }
        return dp[(prices.size()-1) % 2][1];
    }
};

你可能感兴趣的:(代码随想录算法训练营,算法,leetcode,动态规划)