代码随想录算法训练营第五十天|123.买卖股票的最佳时机III、188. 买卖股票的最佳时机 IV

LeetCode 123.买卖股票的最佳时机III

链接:123. 买卖股票的最佳时机 III

思路:

这题的关键在于限制了买入和卖出的次数,而按照在122. 买卖股票的最佳时机 II的做法,仅仅每次累计之前的金额是无法控制买入和卖出次数的,所以有没有办法是能记录次数的?有,只要为每次买入和卖出单独分配一个下标就可以了。定义下标:dp[i][j]表示第i天j状态下所能得到的最大金额,j一共有5个状态:

  • 0:不进行任何操作
  • 1:第一次买入
  • 2:第一次卖出
  • 3:第二次买入
  • 4:第二次卖出

通过以上几个状态,就可以记录在给定的买入和卖出次数下所能获得的最大金额了。然后初始化dp,dp[0][0]表示在当天不进行任何操作,所以没有买入和卖出,所持最大金额为0。dp[0][1]表示在当天进行买入,所持最大金额为-prices[0]。dp[0][2]表示在第一天第一次卖出,因为第一天没有持有股票,当然也无法卖出,当然买了当天股票再卖出也是等于没有卖,所以所持最大金额也是0。dp[0][3]表示第二次买入,这个时候就和当天买入再马上卖出然后又马上再买入了,所以所持最大金额也是-prices[0]。dp[0][4]和dp[0][2]的情况一样,不过多做解释。

接下来比较关键,需要确认递推公式。由于有5个状态,每个状态都需要一个递推公式。

  1. dp[i][0] = dp[i-1][0]。表示什么都不做,所以和前一天一样,其实这一步可以省略
  2. dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1])。进入第一次买入状态的两种情况: 1. 第一次买入,所持有最大金额等于前一天没有买股票的状态,减去当天股票的价格。 2. 前一天已经买入了股票,所持最大金额为前一天买了股票的最大金额
  3. dp[i][2] = max(dp[i-1][1] + prices[i], dp[i-1][2])。进入第一次卖出状态的两种情况:1. 第一次卖出,所持最大金额等于前一天第一次持有股票时的金额dp[i-1][1],加上当天股票的价格prices[i]。 2. 前一天已经卖出了,所持有金额等于前一天卖出的金额dp[i-1][2]。
  4. dp[i][3] = max(dp[i-1][2] - prices[i], dp[i-1][3])。情况和第一次买入一样,唯一的区别是该状态必须由第一次卖出(dp[i-1][2])推导而来,以确保这个状态是第二次买入的状态。
  5. dp[i][4] = max(dp[i-1][3] + prices[i], dp[i-1][4])。情况和第一次卖出一样,唯一的区别是该状态必须由第二次买入(dp[i-1][3])推导而来,以确保这个状态是第二次卖出的状态。

最后返回最后一天最后一次卖出时所持有的最大金额即可。

代码:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        // 定义下标:dp[i][j]表示第i天j状态下所能得到的最大金额
        // j有5个状态:
        // 0:不进行任何操作
        // 1: 第一次买入
        // 2:第一次卖出
        // 3:第二次买入
        // 4:第二次卖出
        vector<vector<int>> dp(prices.size(), vector<int>(5));
        dp[0][1] = -prices[0];
        dp[0][3] = -prices[0];
        for (int i = 1; i < prices.size(); i++)
        { 
            dp[i][0] = dp[i-1][0];
            // 进入第一次买入状态的两种情况: 1. 第一次买入 2. 前一天已经买入了
            dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1]);
            // 进入第一次卖出状态的两种情况:1. 第一次卖出 2. 前一天已经卖出了
            dp[i][2] = max(dp[i-1][1] + prices[i], dp[i-1][2]);
            dp[i][3] = max(dp[i-1][2] - prices[i], dp[i-1][3]);
            dp[i][4] = max(dp[i-1][3] + prices[i], dp[i-1][4]);
        }

        return dp[prices.size()-1][4];
    }
};

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

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

思路:

和上一题有异曲同工之处,dp下标定义以及递推公式都是一样的不再赘述,主要区别注意买卖次数不同时如何操作的。根据买卖次数k,定义dp数组中状态数量为k*2+1个,乘2表示每次都包含买入和卖出两种状态,在奇数状态下买入,偶数状态下卖出。所以递推公式中,j为奇数时对应第一次买入的递推公式,j为偶数时对应第一次卖出的递推公式。每次更新状态j都需要依赖上一个状态j-1,以保证当前状态是由上一个状态推导来的。注意j每次循环都要加2而不是加1。

代码:

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        // 定义下标:dp[i][j]表示第i天j状态下所能得到的最大金额
        // j有k*2+1个状态:
        // 0:不进行任何操作
        // 1: 第一次买入
        // 2:第一次卖出
        // 3:第二次买入
        // 4:第二次卖出
        // ...以此类推
        vector<vector<int>> dp(prices.size(), vector<int>(2*k + 1));
        for (int j = 0; j < k; j++)
            dp[0][j * 2 + 1] = -prices[0];
        for (int i = 1; i < prices.size(); i++)
        { 
            //dp[i][0] = dp[i-1][0];
            for (int j = 1; j < 2*k; j += 2)
            {
                dp[i][j] = max(dp[i-1][j-1] - prices[i], dp[i-1][j]);
                dp[i][j+1] = max(dp[i-1][j] + prices[i], dp[i-1][j+1]);
            }
        }

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值