【python】Leetcode(Dynamic Programming)

70. 爬楼梯(斐波拉习数列)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

  • 示例 1:
    输入: 2
    输出: 2
    解释: 有两种方法可以爬到楼顶。
    1 阶 + 1 阶
    2 阶

  • 示例 2:
    输入: 3
    输出: 3
    解释: 有三种方法可以爬到楼顶。
    1 阶 + 1 阶 + 1 阶
    1 阶 + 2 阶
    2 阶 + 1 阶

思路:这个题和斐波拉系数列一样求解

递归(超时了)

class Solution(object):
    def climbStairs(self, n):
        """
        :type n: int
        :rtype: int
        """
        if n == 1:
            return 1
        elif n == 2:
            return 2
        else:
            return self.climbStairs(n-1)+self.climbStairs(n-2)

改成递推

class Solution(object):
    def climbStairs(self, n):
        """
        :type n: int
        :rtype: int
        """
        nums = [0,1,2]
        for n in range(3,n+1):
            nums.append(nums[n-1]+nums[n-2])
        return nums[n]

746. 使用最小花费爬楼梯(动态规划)

数组的每个索引做为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 costi

每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。

您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。

  • 示例 1:
    输入: cost = [10, 15, 20]
    输出: 15
    解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。

  • 示例 2:
    输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
    输出: 6
    解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。

  • 注意:
    cost 的长度将会在 [2, 1000]。
    每一个 cost[i] 将会是一个Integer类型,范围为 [0, 999]。

思路:动态规划

class Solution(object):
    def minCostClimbingStairs(self, cost):
        """
        :type cost: List[int]
        :rtype: int
        """
        dp0 = cost[0] # 状态的定义
        dp1 = cost[1]
        for i in range(2, len(cost)):
            dpi = min(dp0+cost[i], dp1+cost[i]) # 状态转移方程
            dp0 = dp1
            dp1 = dpi
        return min(dp0, dp1)

我比较习惯下面的写法:

class Solution(object):
    def minCostClimbingStairs(self, cost):
        """
        :type cost: List[int]
        :rtype: int
        """
        f = [0]*(len(cost)) # 状态定义
        f[0] = cost[0]
        f[1] = cost[1]
        for i in range(2,len(cost)):
            f[i] = min(f[i-1]+cost[i],f[i-2]+cost[i]) # 状态转移方程
        return min(f[len(cost)-1],f[len(cost)-2])

279. 完全平方数(动态规划)

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

  • 示例 1:
    输入: n = 12
    输出: 3
    解释: 12 = 4 + 4 + 4.

  • 示例 2:
    输入: n = 13
    输出: 2
    解释: 13 = 4 + 9.

如何判断一个问题是否能用动态规划(Dynamic Programming)解决?

能将大问题拆成几个小问题,且满足

  • 无后效性(未来与过去无关)
  • 最优子结构(大问题的最优解可以由小问题的最优解推出)

coding 的两个关键,状态函数的定义,和状态转移方程

为什么初始化为5,因为四平方和定理——任何一个正整数都可以由4个整数的平方和组成。即对所有n,都有n=a*a+b*b+c*c+d*d,所以对于本题,就不用设置为 inf 了!

class Solution(object):
    def numSquares(self, n):
        """
        :type n: int
        :rtype: int
        """
        f = [5]*(n+1) # 设计状态
        f[0] = 0
        
        for i in range(1,n+1):
            j = 1
            while(j**2<=i):
                f[i] = min(f[i],f[i-j**2]+1) # 状态转移方程
                j+=1
        return f[n]

64. 最小路径和(动态规划)

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

示例:

在这里插入图片描述
思路:和 746 相比,最大的区别是二维的,我们注意一些边界问题的设置即可,比如在第一行的时候,没有上一步,在第一列的时候,没有左一步!

class Solution(object):
    def minPathSum(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        m,n = len(grid), len(grid[0])     
        f = [[0]*n for _ in range(m)] # 状态方程,m行n列,初始化为0
        f[0][0] = grid[0][0] # 赋值

        for i in range(0,m):
            for j in range(0,n):
                if i==0 and j==0: # 跳过 0,0
                    continue
                if i == 0: # 第一行
                    f[i][j] = f[i][j-1] + grid[i][j]  
                elif j == 0: # 第一列
                    f[i][j] = f[i-1][j] + grid[i][j]
                else: #其他情况
                    f[i][j] = min(f[i-1][j],f[i][j-1]) + grid[i][j] # 左和上的最小值 + 当前的代价
        return f[m-1][n-1]

62. 不同路径(动态规划)

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?

在这里插入图片描述
例如,上图是一个7 x 3 的网格。有多少可能的路径?

说明:m 和 n 的值均不超过 100。

  • 示例 1:
    输入: m = 3, n = 2
    输出: 3
    解释:
    从左上角开始,总共有 3 条路径可以到达右下角。
  1. 向右 -> 向右 -> 向下
  2. 向右 -> 向下 -> 向右
  3. 向下 -> 向右 -> 向右
  • 示例 2:
    输入: m = 7, n = 3
    输出: 28

思路:同 64 题,这个题的状态转移方程是,把左和上的路径总和加起来,就是当前位置的路径总和!

class Solution(object):
    def uniquePaths(self, m, n):
        """
        :type m: int
        :type n: int
        :rtype: int
        """
        f = [[0]*n for _ in range(m)] # 初始化 m 行 n 列的列表
        f[0][0] = 1 # 初始化开始的位置
        
        for i in range(m):
            for j in range(n):
                if i==0 and j==0: # 跳过第一个点
                    continue
                elif i==0: # 第一行
                    f[i][j] = f[i][j-1]
                elif j==0: # 第一列
                    f[i][j] = f[i-1][j]
                else:# 其它情况
                    f[i][j] = f[i-1][j]+f[i][j-1] #把左和上的路径加起来
        return f[m-1][n-1]

63. 不同路径 II(动态规划)

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

在这里插入图片描述
网格中的障碍物和空位置分别用 1 和 0 来表示。

说明:m 和 n 的值均不超过 100。

  • 示例 1:
    输入:
    [
    [0,0,0],
    [0,1,0],
    [0,0,0]
    ]
    输出: 2

解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:

  1. 向右 -> 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右 -> 向右

思路:还是先按照 62 的思路来一遍,区别是有障碍物,无障碍物的情况下和62一样处理,有障碍物的时候,障碍物位置的状态方程应该置为0,注意特殊情况,如果起始位置有障碍物,那么直接输出结果 0

class Solution(object):
    def uniquePathsWithObstacles(self, obstacleGrid):
        """
        :type obstacleGrid: List[List[int]]
        :rtype: int
        """
        m,n = len(obstacleGrid),len(obstacleGrid[0])
        
        f = [[0]*n for _ in range(m)]  # 初始化状态函数
        f[0][0] = 1 
        
        if obstacleGrid[0][0] == 1: # 起始位置有障碍物
            return 0
        
        for i in range(m):
            for j in range(n):
                if i==0 and j==0:
                    continue
                elif i==0 and obstacleGrid[i][j]==0: # 没有障碍物的时候第一行
                    f[i][j] = f[i][j-1]
                elif j==0 and obstacleGrid[i][j]==0: # 没有障碍物的时候第一列
                    f[i][j] = f[i-1][j]
                elif obstacleGrid[i][j]==0: # 没有障碍物的时候,某行某列
                    f[i][j] = f[i][j-1] + f[i-1][j] 
                else: # 有障碍物的时候
                    f[i][j] = 0
        return f[m-1][n-1]

72. 编辑距离(动态规划)

给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

插入一个字符
删除一个字符
替换一个字符

示例 1:
输入: word1 = “horse”, word2 = “ros”
输出: 3
解释:
horse -> rorse (将 ‘h’ 替换为 ‘r’)
rorse -> rose (删除 ‘r’)
rose -> ros (删除 ‘e’)

思路:状态转移方程为二维,行列是 word1 和 word2,dp[i][j] 表示 word1 的前 i 个转换成 word2 的前 j 个所需要的操作!
如果 word1[i] = word2[j],那么 dp[i][j] = dp[i-1][j-1]
否则,min(dp[i-1][j-1],dp[i][j-1],dp[i-1][j]) +1 替换,插入,删除

注意第一行和第一列,如下表所示:
在这里插入图片描述

class Solution(object):
    def minDistance(self, word1, word2):
        """
        :type word1: str
        :type word2: str
        :rtype: int
        """
        m = len(word1)
        n = len(word2)
        
        dp = [[0]*(n+1) for _ in range(m+1)]
        
        for i in range(1,m+1): # 初始化第一列
            dp[i][0] = i
        
        for j in range(1,n+1): # 初始化第一行
            dp[0][j] = j
            
        for i in range(1,m+1):
            for j in range(1,n+1):
                if word1[i-1] == word2[j-1]: # 当当前位置的单词相同的时候
                    dp[i][j] = dp[i-1][j-1]
                else: # 当不相同的时候时
                    dp[i][j] = min(dp[i-1][j-1],dp[i][j-1],dp[i-1][j]) +1
                    
        return dp[-1][-1]

303. 区域和检索 - 数组不可变(动态规划)

给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。

示例:

给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()

sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3

说明:

你可以假设数组不可变。
会多次调用 sumRange 方法。

思路:要是自由发挥就比较简单,可惜有格式约束!先来个调用 sum 函数的版本!

class NumArray(object):

    def __init__(self, nums):
        """
        :type nums: List[int]
        """
        self.nums = nums

    def sumRange(self, i, j):
        """
        :type i: int
        :type j: int
        :rtype: int
        """
        return sum(self.nums[i:j+1])

再试试动态规划的版本,注意,题目说 sumRange 要被调用很多次,所以动态规划的方程应该放在__init__ 里面,同过 self 传出来就而可以了,不然会报 oom 的:

class NumArray(object):

    def __init__(self, nums):
        """
        :type nums: List[int]
        """
        f = [0]*len(nums) #状态函数,里面存放起始位置到当前位置的 sum
        if nums: # 这里是防止 nums 为空的情况
            f[0] = nums[0]
        for item in range(1,len(nums)): 
            f[item] = f[item-1] + nums[item] # 状态转移方程
        self.f = f # 把f传出去
        
    def sumRange(self, i, j):
        """
        :type i: int
        :type j: int
        :rtype: int
        """
        if i==0: # 求 (0,5)的 sum,等于 f[5]
            return self.f[j]
        else: # 求(2,5)的 sum,等于 f[5] - f[1]
            return self.f[j]-self.f[i-1]  

# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# param_1 = obj.sumRange(i,j)

304. 二维区域和检索 - 矩阵不可变(动态规划)

给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2)。
在这里插入图片描述
上图子矩阵左上角 (row1, col1) = (2, 1) ,右下角(row2, col2) = (4, 3),该子矩形内元素的总和为 8。

示例:

给定 matrix = [
[3, 0, 1, 4, 2],
[5, 6, 3, 2, 1],
[1, 2, 0, 1, 5],
[4, 1, 0, 1, 7],
[1, 0, 3, 0, 5]
]

sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12

说明:

  • 你可以假设矩阵不可变。
  • 会多次调用 sumRegion 方法。
  • 你可以假设 row1 ≤ row2 且 col1 ≤ col2。

思路:把 303 中的一维升到了二维,可以参考 64 的思路!状态设计为积分图,求解的是区域面积,注意一些边界情况即可!

class NumMatrix(object):

    def __init__(self, matrix):
        """
        :type matrix: List[List[int]]
        """
        if matrix: # 防止 matrix 为空的情况
            m,n = len(matrix),len(matrix[0])
            f = [[0]*n for _ in range(m)]  # 状态函数,积分图
            f[0][0] = matrix[0][0]
            for i in range(m):
                for j in range(n):
                    if i==0 and j==0: # 跳过起点
                        continue
                    elif i==0: # 第一行
                        f[i][j] = f[i][j-1] + matrix[i][j] #左的积分图加当前像素
                    elif j==0: # 第一列
                        f[i][j] = f[i-1][j] + matrix[i][j] #上的积分图加当前像素
                    else: # 其它情况,左点积分图加上的积分图减去左上的积分图加当前像素即为当前的积分图
                        f[i][j] = f[i][j-1] + f[i-1][j] - f[i-1][j-1] + matrix[i][j] # 状态转移方程
        else: # matrix 为空
            f = [[]]
        self.f = f # 传出去

    def sumRegion(self, row1, col1, row2, col2):
        """
        :type row1: int
        :type col1: int
        :type row2: int
        :type col2: int
        :rtype: int
        """
        if row1==0 and col1==0: # 起点是原点,返回终点的积分图
            return self.f[row2][col2]
        elif row1 == 0: # 第一行(相比于非边界情况,排除行减1)
            return self.f[row2][col2] - self.f[row2][col1-1]
        elif col1 == 0: # 第一列(相比于非边界情况,排除列减1)
            return self.f[row2][col2] - self.f[row1-1][col2]
        else: # 用积分图算面积,两次减法,一次加法
            return self.f[row2][col2] + self.f[row1-1][col1-1] - self.f[row2][col1-1] - self.f[row1-1][col2]

# Your NumMatrix object will be instantiated and called as such:
# obj = NumMatrix(matrix)
# param_1 = obj.sumRegion(row1,col1,row2,col2)

343. 整数拆分(动态规划)

给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

  • 示例 1:
    输入: 2
    输出: 1
    解释: 2 = 1 + 1, 1 × 1 = 1。

  • 示例 2:
    输入: 10
    输出: 36
    解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

说明: 你可以假设 n 不小于 2 且不大于 58。

隐藏提示:You may check the breaking results of n ranging from 7 to 10 to discover the regularities.

思路:无从下手,打开隐藏提示,观察 7-10 的规律

参考:https://blog.csdn.net/fuxuemingzhu/article/details/80486238

f(2):1*1 = 1 
f(3):2*1 = 2
f(4):2*2 = 4
f(5):3*2 = 6
f(6):3*3 = 9
f(7):4*3 = 12 = 3*f(7-3)
f(8):3*3*2 = 18 = 3*f(8-3)
f(9):3*3*3 = 27 = 3*f(9-3)
f(10):4*3*3 = 36 = 3*f(10-3)

规律出来了 n ≥ 7 的时候,等于 3*f(n-3),可以看到 3 好像特别猛!为何 3 频频出现在因数里面?证明一下?

假设把 n n n 分为 k k k n / k n/k n/k 份(等周长的四边形中正方形面积最大)

m a x   k n k ⇒ m a x   n k l n k max \ k^{\frac{n}{k}} \Rightarrow max \ \frac{n}{k} lnk max kknmax knlnk

k k k 取导求极值

− n k 2 l n k + n k 2 = 0 -\frac{n}{k^2} lnk + \frac{n}{k^2} = 0 k2nlnk+k2n=0

k = e k = e k=e 的时候值最大,取 2 或者 3

由于 2 ∗ 2 ∗ 2 < 3 ∗ 3 2*2*2 <3 *3 222<33,所以 3

class Solution(object):
    def integerBreak(self, n):
        """
        :type n: int
        :rtype: int
        """
        nums = [0,0,1,2,4,6,9,12]
        f = [0]*(n+1) # 状态的设计
        if n<=7:
            return nums[n]
        else:
            f[0:8] = nums[:]
            for i in range(7,n+1):
                f[i] = f[i-3]*3 # 状态转移方程
            return f[n]

300. 最长上升子序列(动态规划)

给定一个无序的整数数组,找到其中最长上升子序列的长度。

  • 示例:
    输入: [10,9,2,5,3,7,101,18]
    输出: 4
    解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

  • 说明:
    可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
    你算法的时间复杂度应该为 O(n2) 。

思路:动态规划,计算当前结点的最长上升子序列的时候,比较当前数字和前面数字的大小,取小于当前数字的最大序列的值 +1

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        
        n = len(nums)
        dp = [1]*n
        for i in range(n):
            for j in range(i):
                if nums[j]<nums[i]:
                    dp[i] = max(dp[i],dp[j]+1)
        return max(dp)

53. 最大子序和(动态规划)

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

  • 示例:
    输入: [-2,1,-3,4,-1,2,1,-5,4],
    输出: 6
    解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

思路:动态规划,dp[i] = max(nums[i],nums[i]+dp[i-1])

class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        n = len(nums)
        for i in range(1,n):
            nums[i] = max(nums[i],nums[i]+nums[i-1])
        return max(nums)

646. 最长数对链(动态规划)

给出 n 个数对。 在每一个数对中,第一个数字总是比第二个数字小。

现在,我们定义一种跟随关系,当且仅当 b < c 时,数对(c, d) 才可以跟在 (a, b) 后面。我们用这种形式来构造一个数对链。

给定一个对数集合,找出能够形成的最长数对链的长度。你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。

  • 示例 :
    输入: [[1,2], [2,3], [3,4]]
    输出: 2
    解释: 最长的数对链是 [1,2] -> [3,4]

  • 注意:
    给出数对的个数在 [1, 1000] 范围内。

思路:先根据每组数据的第一个数字的大小排序,然后后面的操作和 300 题基本一样了。

class Solution(object):
    def findLongestChain(self, pairs):
        """
        :type pairs: List[List[int]]
        :rtype: int
        """
        pairs = sorted(pairs) # 按照 pairs[:][0] 的大小排序
        f = [0]*(len(pairs)+1) # 状态函数,记录每组的最长子序列
        
        for item in range(1,len(pairs)+1):
            max_seq = 0
            for i in range(1,item):
                if pairs[item-1][0] > pairs[i-1][1]:# 比较大小
                    max_seq = max(max_seq,f[i])
            f[item] = max_seq+1 # 状态转移函数
        
        return max(f)

121. 买卖股票的最佳时机(交易一次,动态规划)

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

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

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

  • 示例 1:
    输入: [7,1,5,3,6,4]
    输出: 5
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
    注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

  • 示例 2:
    输入: [7,6,4,3,1]
    输出: 0
    解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

思路:动态规划,当天最大的利润有两种情况,卖股票了(当天股票价格减去历史股票最低价格),和不卖股票(同前一天的最大利润一样),取两者的最大值即可!注意下边界情况,输入为 [] 的时候!

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        if prices:
            n = len(prices)
            dp = [0]*n
            min_p = prices[0]

            for i in range(1,n):
                min_p = min(min_p,prices[i]) # 记录历史最低股票,这里是最关键的一步
                dp[i] = max(dp[i-1],prices[i]-min_p) # 今天不卖,和今天卖
            return max(dp)
        else:
            return 0

还有一种比较直接的思路,这种也很容易换成上面的 dp 模式

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        fstBuy,fstSell= -sys.maxsize,0
        for i in prices:
            fstBuy = max(fstBuy, -i) # 买最便宜的
            fstSell = max(fstSell, fstBuy + i) # 卖利润最高的,要比 0 高
        return fstSell

122. 买卖股票的最佳时机 II(无限交易,贪心算法)

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

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

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

  • 示例 1:
    输入: [7,1,5,3,6,4]
    输出: 7
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
    随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

  • 示例 2:
    输入: [1,2,3,4,5]
    输出: 4
    解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
    注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
    因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

  • 示例 3:
    输入: [7,6,4,3,1]
    输出: 0
    解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

思路:因为是无限交易(可以当天卖了再买),所以,只要后一天比前一天涨了就买卖一波!

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        n = len(prices)
        result = 0
        for i in range(1,n):
            w = prices[i]-prices[i-1]
            if w>0:
                result += w
        return result

123. 买卖股票的最佳时机 III(最多两次)

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

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

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

  • 示例 1:
    输入: [3,3,5,0,0,3,1,4]
    输出: 6
    解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
    随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。

  • 示例 2:
    输入: [1,2,3,4,5]
    输出: 4
    解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
    注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
    因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

  • 示例 3:
    输入: [7,6,4,3,1]
    输出: 0
    解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。

思路:可以延续 121 的第二种解法,参考 https://blog.csdn.net/qq_38575545/article/details/86239275

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        import sys
        """
        对于任意一天考虑四个变量:
        fstBuy: 在该天第一次买入股票可获得的最大收益 
        fstSell: 在该天第一次卖出股票可获得的最大收益
        secBuy: 在该天第二次买入股票可获得的最大收益
        secSell: 在该天第二次卖出股票可获得的最大收益
        分别对四个变量进行相应的更新, 最后secSell就是最大
        收益值(secSell >= fstSell)
        """
        fstBuy,fstSell= -sys.maxsize,0
        secBuy,secSell= -sys.maxsize,0
        for i in prices:
            fstBuy = max(fstBuy, -i)
            fstSell = max(fstSell, fstBuy + i)
            secBuy = max(secBuy, fstSell - i)
            secSell = max(secSell, secBuy + i)
        return secSell
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值