【LeetCode刷题记录】-简单难度(1)-动态规划(Dynamic Programming)

【LeetCode刷题记录】-简单难度-动态规划(Dynamic Programming)

  • 什么是动态规划
  • 相关题目
    • 1.买卖股票的最佳时机
      • 思路
      • 代码
    • 2.最大子序和
      • 思路
      • 代码
  • 总结

什么是动态规划

动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。 动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。

这是百科上对于动态规划的解释,看起来复杂其实很简单,举个例子:你可以问一个小孩 1 + 1 + 1 = ?,很简单答案是3,那1+1+1+1 = ?,答案也很简单是4,怎样计算更快呢?只需3 + 1即可。我们可以看到如果能记录前一个子问题的结果,避免重复运算,很好运用前一个子问题的结果大大提升效率,这就是动态规划(Dynamic Programming)

相关题目

1.买卖股票的最佳时机

给定一个数组,它的第 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:
    def maxProfit(self, prices: List[int]) -> int:
        buy_point = 0
        max_profit = -float("inf")
        for i in range(1, len(prices)):
            if prices[i] < prices[buy_point]:
                buy_point = i
            else:
                max_profit = max(max_profit, prices[i]-prices[buy_point])
        if max_profit < 0:
            return 0
        else:
            return max_profit

官方

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        minprice = float('inf')
        maxprofit = 0
        for price in prices:
            minprice = min(minprice, price)
            maxprofit = max(maxprofit, price - minprice)
        return maxprofit

对比
官方代码更加简洁,对比之后才能发现其实我的代码可以更好的优化。
1.对比求最小值和最大值问题上,应用max(arg,arg…,arg)min(arg,arg,…,arg),既简洁可读性也强。
2.循环里没必要使用 if…else 语句,然后循环可以从0开始。当初想的是利润的正负要分开,其实没要,第一次最大profit为0,之后的负数利润是不可能比0大的。

2.最大子序和

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

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

进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

思路

可以用如下方法解决,详细分析动态规划

  1. 暴力法,时间复杂度高,为 O ( n 2 ) O(n^2) O(n2)
  2. 动态规划,时间复杂度为 O ( n O(n O(n)
    首先是定义子问题,以i结尾的连续数组的最大值,即 f ( i ) f(i) f(i),最后求 m a x 0 ≤ i ≤ n − 1 { f ( i ) } max_{0 \le i\le n -1} \{f(i)\} max0in1{f(i)}
    现在的问题是如何去求 f ( i ) f(i) f(i),利用动态规划的思想我们要找到 f ( i − 1 ) f(i-1) f(i1) f ( i ) f(i) f(i)之间的关系
    f ( i ) = m a x { f ( i − 1 ) + a i , f ( i ) } f(i) = max\{f(i-1) + a_i, f(i)\} f(i)=max{f(i1)+ai,f(i)},前i个连续数组的最大值要么是第 i − 1 i-1 i1个连续数组最大值与当前值之和,要么是当前值。
    [-2,1,-3]为例:
    f ( − 2 ) = − 2 f(-2) = -2 f(2)=2
    f ( 1 ) = m a x ( 1 , f ( − 2 ) + 1 ) = 1 f(1) = max(1, f(-2)+1) = 1 f(1)=max(1,f(2)+1)=1,因为和的值小,所以以第二个为结尾的连续数组的最大值为1,而不是-1
    f ( − 3 ) = m a x ( − 3 , f ( 1 ) − 3 ) = − 2 f(-3) = max(-3, f(1) - 3) = -2 f(3)=max(3,f(1)3)=2,因为和的值大,所以以第三个结尾的连续数组的最大值为-2
    所以最后 m a x { f ( i ) } = 1 max\{f(i)\} = 1 max{f(i)}=1
  3. 分治法,时间复杂度为 O ( n ) O(n) O(n)
  4. 贪心法,时间复杂度为 O ( n ) O(n) O(n)

暴力法实现的时间复杂度为 O ( n 2 ) O(n^2) O(n2)

代码

动态规划法

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        dy = []
        for i in range(len(nums)):
            if not dy:
                dy.append(nums[i])
            else:
                dy.append(max(nums[i], dy[i-1] + nums[i]))
        return max(dy)

总结

这两道题感觉上是非常相似的,但是第二道题更加复杂,因为要考虑如何去找子问题,所以第二题我并没有独立思考出来。虽然没有思考出来,但是我对动态规划的思想有了更深刻的认识:将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解,可以说第二道题完全就是按照这个思想来的。希望能够利用动态规划的思想独立思考出其他的题目。

你可能感兴趣的:(【LeetCode刷题记录】-简单难度(1)-动态规划(Dynamic Programming))