研习代码 day43 | 动态规划——买卖股票的最佳时机 III IV

一、买卖股票的最佳时机 III(至多能买卖 2 次)

        1.1 题目

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

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

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

示例 1:

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

示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。   
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入:prices = [7,6,4,3,1] 
输出:0 
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。

示例 4:

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

提示:

  • 1 <= prices.length <= 10^5
  • 0 <= prices[i] <= 10^5

        1.2 题目链接

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

        1.3 解题思路和过程想法

        (1)解题思路

        # 分析:当前状态受之前的状态所影响,采用动态规划

        # 数组:当前节点共有五种情况——不操作、第一次持有、第一次不持有、第二次持有、第二次不持有
        # 递推关系:为好理解层次关系,此处采用二维数组表示
                                dp[i][0] = dp[i-1][0]   
                                dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
                                dp[i][2] = max(dp[i-1][2], dp[i-1][1]+prices[i])
                                dp[i][3] = max(dp[i-1][3], dp[i-1][2]-prices[i])
                                dp[i][4] = max(dp[i-1][4], dp[i-1][3]+prices[i])

        # 初始化:为降低时间复杂度,此处采用一维滚顶数组的思想存储过程变量
                         其中 dp_3 和 dp_4 需理解为当天多次交易的结果
                                dp_0 = 0            
                                dp_1 = -prices[0]   
                                dp_2 = 0                    # 理解为当天买卖过一次之后的结果
                                dp_3 = -prices[0]       # 理解为当天经历过买卖过一次之后,又买入的情况
                                dp_4 = 0                    # 理解为当天买卖过两次之后的结果

        # 结果:原本写的是 sum(dp_2, dp_4),但其实 dp_4 中其实是包括了 dp_2 的,因为如果仅买卖过一次后,dp_4 又在当天买卖过一次了

        (2)过程想法

        从动规五部曲的框架着手写代码是比较好写的,着重理解“数组”——状态种数,“递推”——结合实际情形去写递推关系;
        想着有空多回头看看之前写的题目,从框架和实际作用的对比角度去重新审视题目。

        1.4 代码

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # 分析:当前状态受之前的状态所影响,采用动态规划

        # 数组:当前节点共有五种情况——不操作、第一次持有、第一次不持有、第二次持有、第二次不持有
        """
        递推关系:   dp[i][0] = dp[i-1][0]   
                    dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
                    dp[i][2] = max(dp[i-1][2], dp[i-1][1]+prices[i])
                    dp[i][3] = max(dp[i-1][3], dp[i-1][2]-prices[i])
                    dp[i][4] = max(dp[i-1][4], dp[i-1][3]+prices[i])
        """ 
        
        # 初始化:当前状态都由前一状态推演得出
        dp_0 = 0            
        dp_1 = -prices[0]   
        dp_2 = 0            # 理解为当天买卖过一次之后的结果
        dp_3 = -prices[0]   # 理解为当天经历过买卖过一次之后,又买入的情况
        dp_4 = 0            # 理解为当天买卖过两次之后的结果

        for i in range(1, len(prices)):
            dp_1 = max(dp_1, -prices[i])
            dp_2 = max(dp_2, dp_1+prices[i])
            dp_3 = max(dp_3, dp_2-prices[i])
            dp_4 = max(dp_4, dp_3+prices[i])

        # 原本写的是 sum(dp_2, dp_4),但其实 dp_4 中其实是包括了 dp_2 的,因为如果仅买卖过一次后,dp_4 又在当天买卖过一次了
        return dp_4

二、买卖股票的最佳时机 IV(至多能买卖变量 k 次)

        2.1 题目

        给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。

        设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 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 <= 100
  • 1 <= prices.length <= 1000
  • 0 <= prices[i] <= 1000

        2.2 题目链接

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

        2.3 解题思路和过程想法

        (1)解题思路

        # 分析:与上一题相比,只是将至多交易的次数由常量 2 变成了变量 k 。只需总结初始化和递推关系的统一形式,再代入即可。
        # 初始化:当前状态都由前一状态推演得出奇数下标为 -prices[0],偶数下标为 0 
                dp = [0] * (2*k+1)
                for i in range(1, 2*k+1, 2):
                      if i % 2 == 1:
                      dp[i] = -prices[0]
        # 递推关系
                for j in range(1, 2*k+1):
                        if j % 2 == 1:
                                dp[j] = max(dp[j], dp[j-1]-prices[i])
                        else:
                                dp[j] = max(dp[j], dp[j-1]+prices[i])

        (2)过程想法

        从动规五部曲的框架着手写代码是比较好写的,着重理解“数组”——状态种数,“递推”——结合实际情形去写递推关系;
        想着有空多回头看看之前写的题目,从框架和实际作用的对比角度去重新审视题目。

        2.4 代码

class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        # 分析:当前状态受之前的状态所影响,采用动态规划

        # 数组:当前节点共有 2k 种情况——第一次持有、第一次不持有、第二次持有、第二次不持有...
        """
        递推关系:  
                    dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
                    dp[i][2] = max(dp[i-1][2], dp[i-1][1]+prices[i])
                    dp[i][3] = max(dp[i-1][3], dp[i-1][2]-prices[i])
                    dp[i][4] = max(dp[i-1][4], dp[i-1][3]+prices[i])
                    ...
        """ 
        
        # 初始化:当前状态都由前一状态推演得出奇数下标为 -prices[0],偶数下标为 0 
        dp = [0] * (2*k+1)
        for i in range(1, 2*k+1, 2):
            if i % 2 == 1:
                dp[i] = -prices[0]

        for i in range(1, len(prices)):
            for j in range(1, 2*k+1):
                if j % 2 == 1:
                    dp[j] = max(dp[j], dp[j-1]-prices[i])
                else:
                    dp[j] = max(dp[j], dp[j-1]+prices[i])

        return dp[2*k]

你可能感兴趣的:(动态规划,算法,数据结构,python,leetcode)