跟着专注于计算机视觉的AndyJ的妈妈我学算法之每日一题leetcode买卖股票系列题

leetcode买卖股票共有6道题,比较经典,虽然有多种解决方案,但是最重要的通用的一种是动态规划。
Tags array | dynamic-programming | greedy
好了,看下题目综述,其中的6个条件,是不同题包含的。
题目综述:

买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
条件:只许一次交易 / 允许多次交易 / 只许2次交易 / 允许k次交易 / 含冷冻期 / 含手续费
设计一个算法来计算你所能获取的最大利润。
注意:你不能在买入股票前卖出股票。

本质来说,会允许k次交易就好了。值得注意的是含冷冻期的实现。其他的都是子集。
这一个问题,labuladong说的很好,可以参考。应用转态和选择的思路。

这个题,我为数不多的仔细写写吧。看两种情况,一种是可以进行一次操作的,一种是能进行很多次操作的。
用实际例子比较好理解,如下:

比如给出的例子是这样的:
p r i c e s = [ 3 , 2 , 1 , 7 , 9 , 4 , 6 ] prices = [3, 2, 1, 7, 9, 4, 6] prices=[3,2,1,7,9,4,6]

  1. 如过只能进行一次操作。持有为1,未持有为0。过程dp数组如下:
    转态转移方程:
    d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]) dp[i][0]=max(dp[i1][0],dp[i1][1]+prices[i])
    d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , − p r i c e s [ i ] ) dp[i][1] = max(dp[i-1][1], -prices[i]) dp[i][1]=max(dp[i1][1],prices[i])
0(未持有)0(初始化)0006888
1 (持有)-inf(初始化)-3-2-1-1-1-1-1

持有的后面都是-1,代表-1是最低价买入了。
未持有后面都是8,代表后面的都卖不到8了。

  1. 如过只能进行很多次操作。持有为1,未持有为0。过程dp数组如下:
    转态转移方程:
    d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]) dp[i][0]=max(dp[i1][0],dp[i1][1]+prices[i])
    d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i]) dp[i][1]=max(dp[i1][1],dp[i1][0]prices[i])
未持有0(初始化)00068810
持有-inf(初始化)-3-2-1-1-144

思路就是低价买入,高价卖出。赚价差。

再比如 p r i c e s = [ 1 , 9 , 6 , 10 ] prices = [1, 9, 6, 10] prices=[1,9,6,10]
如果只操作一次,那就是1买10卖。如果操作多次,现在9卖,然后买入6再到10卖。

  1. 一次操作
未持有0(初始化)0889
持有-inf(初始化)-1-1-1-1
  1. 多次操作
未持有0(初始化)08812
持有-inf(初始化)-1-122

这个题就是举例子,然后就慢慢懂了,感受到了。

好了,写统一的代码。

class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        n = len(prices)
        if k>(n//2):
            return self.infk(prices)
        # i, k, 0/1; 分别是存i天,k次操作,未持有/持有
        dp = [[[0,0] for _ in range(k+1)] for _ in range(n+1)]
        for j in range(k+1):
            dp[0][j][1] = -float('inf')
        for i in range(1,n+1):
            for j in range(1,k+1):
                dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1]+prices[i-1])
                dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0]-prices[i-1])
        return dp[n][k][0]
    
    def infk(self, prices):
        n = len(prices)
        dp = [[0,0] for _ in range(n+1)]
        dp[0][1] = -float('inf')
        for i in range(1,n+1):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i-1])
            dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i-1])
        return dp[n][0]

注意初始化, d p [ 0 ] [ k ] [ 1 ] dp[0][k][1] dp[0][k][1]是第1天就持有,所以为 − f l o a t ( ′ i n f ′ ) -float('inf') float(inf),不可能,负无穷。

操作k次,转态转移方程:
d p [ i ] [ k ] [ 0 ] = m a x ( d p [ i − 1 ] [ k ] [ 0 ] , d p [ i − 1 ] [ k ] [ 1 ] + p r i c e s [ i ] ) dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1]+prices[i]) dp[i][k][0]=max(dp[i1][k][0],dp[i1][k][1]+prices[i]) # 未持有 = max(上次就未持有,上次持有+卖)
d p [ i ] [ k ] [ 1 ] = m a x ( d p [ i − 1 ] [ k ] [ 1 ] , d p [ i − 1 ] [ k − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i]) dp[i][k][1]=max(dp[i1][k][1],dp[i1][k1][0]prices[i]) # 持有 = max(上次就持有,上次为持有+买)

操作1次,转态转移方程,省掉k,因为初始化为0:
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]) dp[i][0]=max(dp[i1][0],dp[i1][1]+prices[i])
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , − p r i c e s [ i ] ) dp[i][1] = max(dp[i-1][1], -prices[i]) dp[i][1]=max(dp[i1][1],prices[i])

操作无穷次,转态转移方程,省掉k,注意与操作1次的区别,因为k无穷大,所以k-1==k:
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]) dp[i][0]=max(dp[i1][0],dp[i1][1]+prices[i])
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i]) dp[i][1]=max(dp[i1][1],dp[i1][0]prices[i])

含冷冻期,无限次购买,变成i-2,两天前未持有,然后买入:
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]) dp[i][0]=max(dp[i1][0],dp[i1][1]+prices[i])
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 2 ] [ 0 ] − p r i c e s [ i ] ) dp[i][1] = max(dp[i-1][1], dp[i-2][0]-prices[i]) dp[i][1]=max(dp[i1][1],dp[i2][0]prices[i])

含手续费:
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] − f e e ) dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]-fee) dp[i][0]=max(dp[i1][0],dp[i1][1]+prices[i]fee)
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i]) dp[i][1]=max(dp[i1][1],dp[i1][0]prices[i])

好了,就这样了,最好吧答案/状态转移矩阵 记住。。。我捋一会也快晕了。。。不行就看看举的例子,自己也举例子一下。
好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值