跟着专注于计算机视觉的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]

  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])
    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])
0(未持有) 0(初始化) 0 0 0 6 8 8 8
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])
    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])
未持有 0(初始化) 0 0 0 6 8 8 10
持有 -inf(初始化) -3 -2 -1 -1 -1 4 4

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

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

  1. 一次操作
未持有 0(初始化) 0 8 8 9
持有 -inf(初始化) -1 -1 -1 -1
  1. 多次操作
未持有 0(初始化) 0 8 8 12
持有 -inf(初始化) -1 -1 2 2

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

好了,写统一的代码。

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] 是第1天就持有,所以为 f l o a t ( i n f ) -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]) # 未持有 = 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]) # 持有 = 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])
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])

操作无穷次,转态转移方程,省掉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])
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])

含冷冻期,无限次购买,变成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])
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])

含手续费:
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)
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])

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

猜你喜欢

转载自blog.csdn.net/mianjiong2855/article/details/107694500