股票买入卖出问题 leetcode123(最多k次交易)

leetcode123 问题链接 https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/  

给定一些天数的股票价格list, 最多k次买入卖出, 求能获得的最大利润。 (买入前必须先卖出已有的股票)

解决思路:动态规划

令dp[i][j]表示最多i次交易(买入卖出算一次交易),前j天所能获得的最大利润。

dp[i][j] = max(dp[i][j-1],  prices[j] - prices[x] + dp[i-1][x])   (x < j)

其中,逗号左边的dp[i][j-1] 表示第j天不卖出股票,逗号右边表示第j天卖出股票,dp[i][j]将取两者中的较大者。

逗号左边的容易理解,来看一下逗号右边的式子 prices[j] - prices[x] + dp[i-1][x]

其中, prices[j]是第j天卖出股票得到的收入,而在卖出这支股票前,肯定进行过买入的操作,-prices[x]是减去第x天买入所花的钱,由于买入在卖出之前,所以x < j,prices[j] - prices[x] 表示当前这次交易所得的利润。同时还要加上在这次交易之前所获得的最大利润,即最多进行i-1次交易,截止到第x天所能获得的最大利润,这是当前问题的子问题dp[i-1][x]。

由于x<j, x是一个变量,需要采用一些小技巧来降低计算复杂度,否则在遍历i, j的过程中还要对x进行遍历。

令第j-1轮求得的max( dp[i-1][x] - prices[x] )   (x < j) 存储在tmpMax变量中,则在第j轮迭代过程中

dp[i][j] = max( dp[i][j-1],  prices[j] - prices[x] + dp[i-1][x] )    (x < j)

          = max( dp[i][j-1],  prices[j] + max( dp[i-1][x]- prices[x] ) (x < j)  )   

          =  max(dp[i][j-1],  prices[j] + tmpMax) 

在第j轮结束后,更新tmpMax的值以备第j+1轮使用: tmpMax = max(tmpMax, dp[i-1][j]- prices[j] )

python代码为:

class Solution:
    def maxProfit(self, k, prices):
        if not prices: return 0
        dp = [ [0]*len(prices) for i in range(k+1) ]
        # 初始化,k=0时候没有交易,利润为0; j=0时只有一天的数据,无论如何利润为0
        for i in range(1, k+1):
            tmp = dp[i-1][0] - prices[0] #initial
            for j in range(1, len(prices)):
                dp[i][j] = max(dp[i][j-1], prices[j] + tmp)
                tmp = max(tmp, dp[i-1][j] - prices[j])
        return dp[-1][-1]
        

时间复杂度O(kn). 空间复杂度O(kn), k是最多交易次数,n是股票价格列表的长度(天数)。当k很大时,内存会溢出。时间也会超限。下面对空间和时间进行优化。

首先对空间进行优化,由于dp数组每一行更新只用到了上一行的数据,因此dp数组可以由O(kn)降为O(n)

class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        if not prices: return 0
        dp = [0] * len(prices)
        for i in range(1, k+1):
            tmp = dp[0] - prices[0]
            for j in range(1, len(prices)):
                tmp2 = max(tmp, dp[j] - prices[j])
                dp[j] = max(dp[j-1], prices[j] + tmp)
                tmp = tmp2
        return dp[-1]
                
        

改成这样之后,发现内存满足了要求,但是时间超限,继续优化时间。思路是当最大交易次数超过数组长度的一半时,意味着对于当前数组可以进行无限次的交易(卖出之前先要买入,买入之前手里不能有其他股票)。这时候问题转化成leetcode122,有成熟的O(n)时间复杂度的解法。因此加入一个判断,把问题分成k > len(prices)//2和k<= len(prices)//2两种情况来节约一些时间。

class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        if not prices: return 0
        def helper(prices): #可以无限次买入卖出
            profit = 0
            for i in range(1, len(prices)):
                if prices[i] > prices[i-1]:
                    profit += prices[i]-prices[i-1]
            return profit

        if k > len(prices)//2:
            return helper(prices)
        dp = [0] * len(prices)
        for i in range(1, k+1):
            tmp = dp[0] - prices[0]
            for j in range(1, len(prices)):
                tmp2 = max(tmp, dp[j] - prices[j])
                dp[j] = max(dp[j-1], prices[j] + tmp)
                tmp = tmp2
        return dp[-1]
                

这时时间复杂度和空间复杂度都满足了要求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值