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

Posted 炫云云

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了188. 买卖股票的最佳时机 IV相关的知识,希望对你有一定的参考价值。

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

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

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

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

示例 1:

输入:k = 2, prices = [2,4,1]
输出:2

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

动态规划

与其余的股票问题类似,我们使用一系列变量存储「买入」的状态,再用一系列变量存储「卖出」的状态,通过动态规划的方法即可解决本题。

我们用 buy [ i ] [ j ] [i][j] [i][j] 表示对于数组 p r i c e s [ 0.. i ] prices[0 . . i] prices[0..i] 中的价格而言, 进行恰好 j j j 笔交易,并且当前手上持有一 支股票,这种情况下的最大利润;用 sell ⁡ [ i ] [ j ] \\operatorname{sell}[i][j] sell[i][j] 表示恰好进行 j j j​ 笔交易,并且当前手上不持有股票, 这种情况下的最大利润。

那么我们可以对状态转移方程进行推导。对于 b u y [ i ] [ j ] b u y[i][j] buy[i][j]​​, 我们考虑当前手上持有的股票是否是在第 i i i​​ 天买入的。如果是第 i i i​​ 天买入的,那么在第 i − 1 i-1 i1​​ 天时,我们手上不持有股票, 对应状态 sell ⁡ [ i − \\operatorname{sell}[i- sell[i​​ 1 ] [ j ] 1][j] 1][j]​​, 并且需要扣除 prices [ i ] [i] [i]​​ 的买入花费;如果不是第 i i i​​ 天买入的,那么在第 i − 1 i-1 i1​​ 天时,我们手上 持有股票,对应状态 buy [ i − 1 ] [ j ] [i-1][j] [i1][j]​​​ 。那么我们可以得到状态转移方程:
 buy  [ i ] [ j ] = max ⁡ {  buy  [ i − 1 ] [ j ] , sell ⁡ [ i − 1 ] [ j ] −  price  [ i ] } \\text { buy }[i][j]=\\max \\{\\text { buy }[i-1][j], \\operatorname{sell}[i-1][j]-\\text { price }[i]\\}  buy [i][j]=max{ buy [i1][j],sell[i1][j] price [i]}
同理对于 sell ⁡ [ i ] [ j ] \\operatorname{sell}[i][j] sell[i][j]​, 如果是第 i i i​ 天卖出的,那么在第 i − 1 i-1 i1​ 天时,我们手上持有股票, 对应状态 buy [ i − 1 ] [ j − 1 ] [i-1][j-1] [i1][j1]​, 并且需要增加 prices [ i ] [i] [i]​ 的卖出收益;如果不是第 i i i​ 天卖出的,那么在第 i − 1 i-1 i1​ 天 时,我们手上不持有股票,对应状态 sell ⁡ [ i − 1 ] [ j ] \\operatorname{sell}[i-1][j]_{ } sell[i1][j]​ 那么我们可以得到状态转移方程:
sell ⁡ [ i ] [ j ] = max ⁡ { sell ⁡ [ i − 1 ] [ j ] ,  buy  [ i − 1 ] [ j − 1 ] +  price  [ i ] } \\operatorname{sell}[i][j]=\\max \\{\\operatorname{sell}[i-1][j], \\text { buy }[i-1][j-1]+\\text { price }[i]\\} sell[i][j]=max{sell[i1][j], buy [i1][j1]+ price [i]}
由于在所有的 n n n​ 天结束后,手上不持有股票对应的最大利润一定是严格由于手上持有股票对应的最大利润的,然而完成的交易数并不是越多越好(例如数组 prices 单调递减,我们不进行任何交易才 是最优的),因此最终的答案即为 sell ⁡ [ n − 1 ] [ 0.. k ] \\operatorname{sell}[n-1][0 . . k] sell[n1][0..k]​ 中的最大值。

细节

在上述的状态转移方程中,确定边界条件是非常重要的步骤。我们可以考虑将所有的 b u y [ 0 ] [ 0.. k ] b u y[0][0 . . k] buy[0][0..k] 以 及 sell ⁡ [ 0 ] [ 0.. k ] \\operatorname{sell}[0][0 . . k] sell[0][0..k] 设置为边界。

对于 b u y [ 0 ] [ 0.. k ] b u y[0][0 . . k] buy[0][0..k], 由于只有 prices ⁡ [ 0 ] \\operatorname{prices}[0] prices[0] 唯一的股价,因此我们不可能进行过任何交易, 那么我们可以 将所有的 buy [ 0 ] [ 1.. k ] [0][1 . . k] [0][1..k] 设置为一个非常小的值, 表示不合法的状态。而对于 b u y [ 0 ] [ 0 ] b u y[0][0] buy[0][0], 它的值为 − - prices [ 0 ] [0] [0], 即「我们在第 0 天以 prices [ 0 ] [0] [0] 的价格买入股票」是唯一满足手上持有股票的方法。

对于 sell ⁡ [ 0 ] [ 0.. k ] \\operatorname{sell}[0][0 . . k] sell[0][0..k], 同理我们可以将所有的 sell ⁡ [ 0 ] [ 1.. k ] \\operatorname{sell}[0][1 . . k] sell[0][1..k] 设置为一个非常小的值, 表示不合法的状态。 而对于 sell ⁡ [ 0 ] [ 0 ] \\operatorname{sell}[0][0] sell[0][0], 它的值为 0 , 即「我们在第 0 天不做任何事」是唯一满足手上不持有股票的方法。

在设置完边界之后,我们就可以使用二重循环, 在 i ∈ [ 1 , n ) , j ∈ [ 0 , k ] i \\in[1, n), j \\in[0, k] i[1,n),j[0,k] 的范围内进行状态转移。需要 注意的是, sell ⁡ [ i ] [ j ] \\operatorname{sell}[i][j] sell[i][j] 的状态转移方程中包含 b u y [ i − 1 ] [ j − 1 ] b u y[i-1][j-1] buy[i1][j1], 在 j = 0 j=0 j=0 时其表示不合法的状态, 因此 在 j = 0 j=0 j=0 时,我们无需对 sell ⁡ [ i ] [ j ] \\operatorname{sell}[i][j] sell[i][j] 进行转移, 让其保持值为 0 即可。

最后需要注意的是,本题中 k k k 的最大值可以达到 1 0 9 10^{9} 109, 然而这是毫无意义的, 因为 n n n 天最多只能进 行 ⌊ n 2 ⌋ \\left\\lfloor\\frac{n}{2}\\right\\rfloor 2n 笔交易,其中 ⌊ x ⌋ \\lfloor x\\rfloor x 表示对 x x x 向下取整。因此我们可以将 k k k ⌊ n 2 ⌋ \\left\\lfloor\\frac{n}{2}\\right\\rfloor 2n​ 取较小值之后再进行动态规 划。

class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        if not prices:
            return 0
        
        n = len(prices)
        k = min(k,n//2)
        buy = [[0] *(k+1) for _ in range(n)]
        sell = [[0]*(k+1) for _ in range(n)]

        buy[0][0], sell[0][0] = -prices[0], 0

        for i in range(1,k+1):
            buy[0][i] = sell[0][i] = float("-inf")
        
        for i in range(1,n+1):
            buy[i][0] = max(buy[i - 1][0] , sell[i-1][0]-prices[i]) 
            for j in range(1,k+1):
                buy[i][j] = max(buy[i - 1][j] , sell[i-1][j]-prices[i])
                sell[i][j] = max(sell[i-1][j], buy[i-1][j-1]+prices[i])
        return max(sell[-1])


注意到在状态转移方程中, b u y [ i ] [ j ] buy[i][j] buy[i][j] sell ⁡ [ i ] [ j ] \\operatorname{sell}[i][j] sell[i][j] 都从 buy [ i − 1 ] [ . . ] [i-1][. .] [i1][..] 以及 sell ⁡ [ i − 1 ] [ . . ] \\operatorname{sell}[i-1][. .] sell[i以上是关于188. 买卖股票的最佳时机 IV的主要内容,如果未能解决你的问题,请参考以下文章

[Leetcode188] 买卖股票的最佳时机IV 动态规划 解题报告

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

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

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

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

算法: 买卖股票的最佳时机 IV 188. Best Time to Buy and Sell Stock IV