(LeetCode)动态规划

本文深入讲解动态规划在解决路径寻找、最优解问题中的应用,包括买卖股票、最大子序和、爬楼梯、打家劫舍、最小路径和及不同路径等经典问题的算法解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0. 总结

一般求有多少种路径最优路径问题,就用动态规划来解决。动态规划的几个要素:

  1. 递推公式:i位置的解如何由i-1位置、i-2位置(非必须)、nums[i]推导出来;
  2. dp向量(一维动态规划)或者dp矩阵(二维动态规划):动态规划用空间换时间,所以一定会存在dp向量或dp矩阵;但值得注意的是,一维动态规划可以将dp向量实现为常数变量二维动态规划可以将dp矩阵实现为dp向量,这有利于降低空间复杂度;也就是说,下面1、2、3、4题标准做法是使用一个dp向量,5、6题的dp矩阵可以用dp向量替换;
  3. 初始值:递推的时候初始值要考虑一下;
  4. 递推方向:可以选择从前往后或者从后往前递推;

1. 买卖股票的最佳时机

121. best-time-to-buy-and-sell-stock
在这里插入图片描述

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int

        笨办法
            暴力求解,两重循环
        
        聪明法
            动态规划,前n天最大利润=max(前n-1天最大利润, 当前价格 - 前n-1天最小价格)
            dp[i] = max(
                dp[i-1], 
                price[i] - min(price_{0~i-1})
            )
        """
        if not prices:
            return 0
        
        min_price = prices[0]
        dp = [0] * len(prices)
        for i, price in enumerate(prices):
            if i == 0:
                continue
            dp[i] = max(dp[i-1], price-min_price)
            min_price = min(min_price, price)
        return dp[-1]

2. 最大子序和

53. maximum-subarray
在这里插入图片描述

class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int

        笨办法
            暴力求解
        
        聪明法
            动态规划
            1. 迭代n次,求出以位置i为终止的n个最大和序列:
               以位置i为终止的最大子序列和 = max(以位置i-1为终止的最大子序列和+第i个数, 第i个数)
               即:以第i个元素为终止时,考虑与前面合并或者断开;
            2. 想要的和最大的子序列就在1所得到的候选里面;
        """
        cand_sum, sum = nums[0], nums[0]  # cand_sum为sum提供候选
        for num in nums[1:]:
            cand_sum = max(cand_sum+num, num)
            sum = max(sum, cand_sum)
        return sum

		# dp = [0] * len(nums)
        # dp[0] = nums[0]
        # for i, num in enumerate(nums):
        #     if i == 0:
        #         continue
        #     dp[i] = max(dp[i-1]+num, num)
        # return max(dp)

3. 爬楼梯

70. climbing-stairs
在这里插入图片描述

class Solution(object):
    def climbStairs(self, n):
        """
        :type n: int
        :rtype: int

        动态规划
            到第i阶的方法数 = 到第i-1阶的方法数 + 到第i-2阶的方法数
            因为要跨到第i阶,必须是 从第i-1阶1次跨入 或者 从第i-2阶1次跨入
            dp[i] = dp[i-1] + dp[i-2]
        
        笨办法
            递归
            原因以上
            if n < 2:
                return 1
            return self.climbStairs(n-1) + self.climbStairs(n-2)
        """
        if n < 3:
            return n
        x1, x2 = 2, 1  # x1表示到第i-1阶的方法数,x2表示到第i-2阶的方法数
        x = 0
        for i in range(3, n+1):
            x = x1 + x2
            x2 = x1
            x1 = x
        return x
        
		# if n == 1:
        #     return 1
        # dp = [0] * (n+1)
        # dp[1], dp[2] = 1, 2
        # for i in range(3, n+1):
        #     dp[i] = dp[i-1] + dp[i-2]
        # return dp[-1]


4. 打家劫舍

198. house-robber
在这里插入图片描述

class Solution(object):
    def rob(self, nums):
        """
        :type nums: List[int]
        :rtype: int

        聪明解法
            动态规划,到i房间得到的最高金额 = max(到i-1房间最高金额, 到i-2房间最高金额+i房间的金额)
            dp[i] = max(dp[i-1], dp[i-2]+nums[i])
        """
        if not nums:
            return 0
        if len(nums) <= 2:
            return max(nums)
        
        x1, x2 = max(nums[:2]), nums[0]
        x = 0
        for num in nums[2:]:
            x = max(x1, x2+num)
            x2 = x1
            x1 = x
        return x

		# if len(nums) == 1:
        #     return nums[0]
        # dp = [0] * len(nums)
        # dp[0], dp[1] = nums[0], max(nums[0], nums[1])
        # for i in range(2, len(nums)):
        #     dp[i] = max(dp[i-1], dp[i-2]+nums[i])
        # return dp[-1]

5. 最小路径和

64. minimum-path-sum
在这里插入图片描述

class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        """
        二维动态规划
        dp[i,j] = min(dp[i-1,j] + grid[i,j], dp[i,j-1] + grid[i,j]) 
        """
        row_cnt, col_cnt = len(grid), len(grid[0])
        dp = [[0] * col_cnt] * row_cnt
        dp[0][0] = grid[0][0]
        for i in range(0, row_cnt):
            for j in range(0, col_cnt):
                if i == j == 0:
                    continue
                if i == 0:
                    dp[i][j] = dp[i][j-1] + grid[i][j]
                elif j == 0:
                    dp[i][j] = dp[i-1][j] + grid[i][j]
                else:
                    dp[i][j] = min(dp[i-1][j] + grid[i][j], dp[i][j-1] + grid[i][j]) 
        return dp[-1][-1]

6. 不同路径

62. unique-paths
在这里插入图片描述
在这里插入图片描述

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        """
        二维动态规划
        dp[i][j] = dp[i-1][j] + dp[i][j-1]
        """
        dp = [[0] * n] * m
        dp[0][0] = 1
        for i in range(m):
            for j in range(n):
                if i == j == 0:
                    continue
                if i == 0:
                    dp[i][j] = dp[i][j-1]
                elif j == 0:
                    dp[i][j] = dp[i-1][j]
                else:
                    dp[i][j] = dp[i-1][j] + dp[i][j-1]
        return dp[-1][-1]

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值