【leetcode刷题】121. 买卖股票的最佳时机(5题汇总)(python3)

121. 买卖股票的最佳时机(简单)

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

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # # 1、暴力法,两次遍历:求得所有的收益值取最大值(over time)
        # max_p = 0
        # for i in range(len(prices)):
        #     for j in range(i,len(prices)):
        #         tmp = prices[j]-prices[i]
        #         max_p = max(tmp, max_p)
        # return max_p

        # # 2、暴力法优化:一次遍历,每个元素后面的最大值(8272ms)
        # max_p = 0
        # for i in range(len(prices)-1):
        #     tmp = max(prices[i+1:])
        #     max_p = max(tmp-prices[i], max_p)
        # return max_p

        # 3、动态规划(44ms)
        # 最后的元素0表示没有持有,1表示持有;最后一天是没有持有状态
        # 初始值没有持有收益为0,持有收益为负无穷表示不可能
        dp_i_0, dp_i_1 = 0,  float('-inf')
        # 遍历每一天的情况:没有持有、持有两种可能的最大收益
        for i in range(len(prices)):
            # 当天没有表示前一天没有,或者当天卖出(当天利润为price[i]);
            # 当天持有表示前一天就有,或者当天买入(当天利润为-price[i])
            dp_i_0=max(dp_i_0, dp_i_1+prices[i])
            dp_i_1=max(dp_i_1, -prices[i])
        return dp_i_0

针对上题方法3,同方法(3D动态规划)类推以下问题。

(1)理解动态规划方法:

dp[i][k][0 or 1]
0 <= i <= n-1, 1 <= k <= K
n 为天数,大 K 为最多交易数
此问题共 n × K × 2 种状态,全部穷举就能搞定。

for 0 <= i < n:
    for 1 <= k <= K:
        for s in {0, 1}:
            dp[i][k][s] = max(buy, sell, rest)

(2)状态转移方程

dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
              max(   选择 rest  ,           选择 sell      )

解释:今天我没有持有股票,有两种可能:
要么是我昨天就没有持有,然后今天选择 rest,所以我今天还是没有持有;
要么是我昨天持有股票,但是今天我 sell 了,所以我今天没有持有股票了。

dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
              max(   选择 rest  ,           选择 buy         )

解释:今天我持有着股票,有两种可能:
要么我昨天就持有着股票,然后今天选择 rest,所以我今天还持有着股票;
要么我昨天本没有持有,但今天我选择 buy,所以今天我就持有股票了。

(3)basecase

dp[-1][k][0] = 0
解释:因为 i 是从 0 开始的,所以 i = -1 意味着还没有开始,这时候的利润当然是 0 。
dp[-1][k][1] = -infinity
解释:还没开始的时候,是不可能持有股票的,用负无穷表示这种不可能。
dp[i][0][0] = 0
解释:因为 k 是从 1 开始的,所以 k = 0 意味着根本不允许交易,这时候利润当然是 0 。
dp[i][0][1] = -infinity
解释:不允许交易的情况下,是不可能持有股票的,用负无穷表示这种不可能。

(4)状态转移方程总结:

base case:
dp[-1][k][0] = dp[i][0][0] = 0
dp[-1][k][1] = dp[i][0][1] = -infinity

状态转移方程:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])

第1题是k=1

122. 买卖股票的最佳时机 II(简单)k=float("inf")

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

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

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

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        dp_i_0, dp_i_1 = 0, float("-inf")
        for i in range(len(prices)):
            tmp = dp_i_0
            dp_i_0 = max(dp_i_0, dp_i_1+prices[i])
            dp_i_1 = max(dp_i_1,tmp-prices[i])
        return dp_i_0

309. 最佳买卖股票时机含冷冻期(中等)k=float("-inf")+cooldown

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        dp_i_0, dp_i_1 = 0, float("-inf")
        dp_i_pre2_0 = 0 # 冷却1天,当天的持有状态,可能为两天前的未持有状态到当天售出
        for i in range(len(prices)):
            tmp = dp_i_0 # 第i-1天的未持有状态
            dp_i_0 = max(dp_i_0, dp_i_1+prices[i]) # 第i天的未持有状态
            dp_i_1 = max(dp_i_1,dp_i_pre2_0-prices[i]) # 第i天的持有状态
            dp_i_pre2_0 = tmp # 将第i-1天的未持有状态赋值给第i-2天,作为(i+1)-2天的未持有状态
        return dp_i_0

714. 买卖股票的最佳时机含手续费(中等)k=float("inf") with fee

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每次交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        dp_i_0, dp_i_1 = 0, float("-inf")
        for i in range(len(prices)):
            tmp = dp_i_0
            dp_i_0 = max(dp_i_0, dp_i_1+prices[i]-fee)
            dp_i_1 = max(dp_i_1, tmp-prices[i])
        return dp_i_0

123. 买卖股票的最佳时机 III(困难)k=2

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

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

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

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        dp_i_1_0, dp_i_1_1 = 0, float("-inf")
        dp_i_2_0, dp_i_2_1 = 0, float("-inf")
        for i in range(len(prices)):
            dp_i_2_0 = max(dp_i_2_0, dp_i_2_1 + prices[i])
            # 可能是前一天没有持有,今日买入,今日的交易次数为k,则前一天的交易次数为k-1
            # 统一买入时计入次数,也可卖出时计入次数
            dp_i_2_1 = max(dp_i_2_1, dp_i_1_0 - prices[i]) 
            dp_i_1_0 = max(dp_i_1_0, dp_i_1_1 + prices[i])
            dp_i_1_1 = max(dp_i_1_1, dp_i_0_0 - prices[i]) # 此处dp_i_0_0=0,可以不写或循环前定义初始值
        return dp_i_2_0

大牛原文参考链接:参考链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值