[leetcode刷题]力扣刷题7之动态规划(DP)详解

动态规划(DP)是一种通过将问题分解为子问题,并通过保存子问题的结果,避免重复计算,最终解决复杂问题的优化方法。其核心思想就是通过存储子问题的解,从而避免重复计算。

动态规划的基本步骤

  1. 定义状态

    • 确定动态规划的状态是什么,通常通过一个数组(或表格)dp[] 来表示问题的不同子问题的解。
  2. 状态转移方程

    • 确定状态之间的关系,即如何通过已知的子问题解推导出当前问题的解。这就是状态转移方程,是动态规划的核心。
  3. 初始化

    • 对于某些基础状态(通常是最小规模的问题),需要进行初始化。
  4. 计算顺序

    • 通常,动态规划是从最小的子问题开始,逐步计算更大的子问题的解。
  5. 返回结果

    • 最终问题的解通常存储在数组的最后一个位置,或者是某些特定的子问题状态。

我们通过一些题目,逐渐深入的讲解。

例1

零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

我们按照上面动态规划基本步骤的思路来解决这个题。

步骤1:定义状态

我们需要定义一个数组 dp 来存储每个金额所需的最少硬币数。

  • 状态dp[i] 表示凑成金额 i 所需的最少硬币数。
    • dp[0] = 0,表示凑成金额 0 所需的硬币数是 0。
    • dp[i] 的值应为从 dp[i - coin] + 1(其中 coin 是硬币的面值)中挑选出最小的值。

步骤2:状态转移方程

我们需要通过所有硬币来更新每个 dp[i] 的值,状态转移方程为:

dp[i]=min⁡(dp[i],dp[i−coin]+1)dp[i] = \min(dp[i], dp[i - coin] + 1)dp[i]=min(dp[i],dp[i−coin]+1)

其中 i 是当前目标金额,coin 是硬币的面值。如果当前的金额 i 大于等于 coin,我们就可以通过 dp[i - coin] 加 1 来更新 dp[i]

步骤3:初始化

  • dp[0] = 0,即凑成 0 元需要 0 个硬币。
  • 对于其他金额 i,我们初始化为一个很大的值(例如 float('inf')),表示当前金额无法凑成。

步骤4:计算顺序

我们从金额 1amount 依次计算每个 dp[i],对于每个 i,我们遍历所有的硬币面值 coin,依次计算最小的硬币数。

步骤5:返回结果

最后我们检查 dp[amount] 是否仍然为初始值 float('inf'),如果是,说明无法凑成该金额,返回 -1;否则返回 dp[amount],即为最少的硬币数。

代码实现

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # 初始化 dp 数组,初始值为正无穷,表示无法凑成
        dp = [float('inf')] * (amount + 1)
        dp[0] = 0  # 金额为 0 时,所需硬币数为 0

        # 动态规划,计算每个金额的最小硬币数
        for i in range(1, amount + 1):
            for coin in coins:
                if i >= coin:
                    dp[i] = min(dp[i], dp[i - coin] + 1)

        # 如果 dp[amount] 仍然为无穷大,表示无法凑成,返回 -1
        return dp[amount] if dp[amount] != float('inf') else -1

例2

爬楼梯

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

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

解题思路:典型的动态规划题,n阶台阶,第一步只能走1步或2步,剩下则为n-1阶或n-2阶楼梯,典型的斐波那契数列。n阶台阶的方法=n-1阶台阶的方法+n-2阶台阶的方法

代码实现

class Solution:
    def climbStairs(self, n: int) -> int:
        if n==1:
            return 1
        dp=[0]*(n+1)
        dp[1]=1
        dp[2]=2
        for i in range(3,n+1):
            dp[i]=dp[i-1]+dp[i-2]
        return dp[n]

这段代码用了 O(n) 的空间来存储 dp 数组,但实际上我们只需要存储前两个状态 dp[i-1]dp[i-2],因此可以将空间复杂度优化为 O(1)

更优的解法

class Solution:
    def climbStairs(self, n: int) -> int:
        if n==1:
            return 1
        elif n==2:
            return 2
        prev1,prev2=2,1
        for i in range(3,n+1):
            curr=prev1+prev2
            prev2=prev1
            prev1=curr
        return curr

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值