动态规划练习题①

70. 爬楼梯

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

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

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶

示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶

class Solution:
    def climbStairs(self, n: int) -> int:
        """
        1. 递归
        假设刚开始站在下标为0的台阶,现在要爬到下标为n的台阶
        dfs(i)表示爬到下标为i的台阶的方法数
        dfs(i)可以表示为dfs(i-1)与dfs(i-2)的台阶总和,这样就分成了子问题
        
        def dfs(i):
            # dfs(0)只有一种方法 ———— 不爬;dfs(1)有一种方法,也就是从坐标为0爬一步到坐标为1
            if i<=1:
                return 1
            return dfs(i-1)+dfs(i-2)
        return dfs(n)
        """

        """
        2. 记忆化搜索
        cache=[-1]*(n+1)
        def dfs(i):
            if i<=1:
                return 1
            if cache[i]!=-1:
                return cache[i]
            res=dfs(i-1)+dfs(i-2)
            cache[i]=res
            return res
        return dfs(n)
        """

        """
        3. 递推
        dp=[0]*(n+1)
        dp[0]=1
        dp[1]=1
        for i in range(2,n+1):
            dp[i]=dp[i-2]+dp[i-1]
        return dp[-1]
        """

        """
        4. 优化空间复杂度为O(1)
        i=1
        j=1
        for _ in range(2,n+1):
            k=i+j
            i=j
            j=k
        return k
        """


746. 使用最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

示例 1:

输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。

示例 2:

输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
- 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
- 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
- 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        """
        1. 递推
        dfs(i)为爬到下标为n台阶时的最小花费

        n=len(cost)
        def dfs(i):
            if i<=1:
            # 站在下标为0或1时不需要花费
                return 0
            return min(dfs(i-1)+cost[i-1],dfs(i-2)+cost[i-2])
        return dfs(n)
        """

        """
        2. 记忆化搜索
        n=len(cost)
        cache=[-1]*(n+1)
        def dfs(i):
            if i<=1:
                return 0
            if cache[i]!=-1:
                return cache[i]
            res=min(dfs(i-1)+cost[i-1],dfs(i-2)+cost[i-2])
            cache[i]=res
            return res
        return dfs(n)
        """

        """
        3. 动态规划
        n=len(cost)
        dp=[0]*(n+1)
        for i in range(2,n+1):
            dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
        return dp[-1]
        """

        """
        4. 优化空间复杂度为O(1)
        n=len(cost)
        i,j=0,0
        for x in range(2,n+1):
            k=min(i+cost[x-2],j+cost[x-1])
            i=j
            j=k
        return k
        """


377. 组合总和 Ⅳ

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。

示例 1:

输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。

示例 2:

输入:nums = [9], target = 3
输出:0

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        """
        1. 递归
        dfs(i)表示为总和为target的元素组合的个数
        将此题抽象为爬楼梯,现在要找出爬到target阶的方法总数
        比如当使用了nums[0]时,就变成了要找出爬到target-nums[0]阶的方法总数的子问题
        当target减到0时,此时就找到了一种方法,返回1

        def dfs(target):
            if target == 0:
                return 1
            tol = 0
            for num in nums:
                if num <= target:
                    tol += dfs(target - num)
            return tol
        return dfs(target)
        """

        """
        2. 记忆化搜索
        n = len(nums)
        cache = [0] * (target + 1)
        def dfs(target):
            if target == 0:
                return 1
            if cache[target] > 0:
                return cache[target]
            tol = 0
            for num in nums:
                if num <= target:
                    tol += dfs(target - num)
            cache[target] = tol
            return tol
        return dfs(target)
        """

        """
        3. 递推
        dp[i]表示目标值为i的方法总和

        n = len(nums)
        dp = [0] * (target + 1)
        dp[0] = 1  # 初始化,目标值为0只有一种组合,即空集
        for i in range(1, target + 1):
            for num in nums:
                if num <= i:  # 如果当前数字小于目标值,则等于dp[i-num]的总和
                    dp[i] += dp[i - num]
        return dp[-1]
        """


2466. 统计构造好字符串的方案数

给你整数 zeroonelowhigh ,我们从空字符串开始构造一个字符串,每一步执行下面操作中的一种:

  • '0' 在字符串末尾添加 zero 次。
  • '1' 在字符串末尾添加 one 次。

以上操作可以执行任意次。

如果通过以上过程得到一个 长度lowhigh 之间(包含上下边界)的字符串,那么这个字符串我们称为 字符串。

请你返回满足以上要求的 不同 好字符串数目。由于答案可能很大,请将结果对 109 + 7 取余 后返回。

示例 1:

输入:low = 3, high = 3, zero = 1, one = 1
输出:8
解释:
一个可能的好字符串是 "011" 。
可以这样构造得到:"" -> "0" -> "01" -> "011" 。
从 "000" 到 "111" 之间所有的二进制字符串都是好字符串。

示例 2:

输入:low = 2, high = 3, zero = 1, one = 2
输出:5
解释:好字符串为 "00" ,"11" ,"000" ,"110" 和 "011" 。

class Solution:
    def countGoodStrings(self, low: int, high: int, zero: int, one: int) -> int:
        # dfs表示用one个字符或者zero个字符所组成长度为i的字符串方法数目
        # @lru_cache(None)
        # def dfs(i):
        #     if i < 0:
        #         return 0
        #     if i == 0:
        #         # 字符串长度为0时,表示该种方案合法
        #         return 1
        #     # 变为组成长度为i-one和i-zero的字符串方法个数的子问题
        #     return dfs(i - one) + dfs(i - zero) % (10**9 + 7)

        # ans = 0
        # for i in range(low, high + 1):
        #     ans += dfs(i)
        # return ans % (10**9 + 7)

        dp = [0] * (high + 1)  # dp[i]表示组成长度为i的字符串的方案数
        dp[0] = 1
        for i in range(1, high + 1):
            if i >= zero:
                dp[i] += dp[i - zero] % (10**9 + 7)
            if i >= one:
                dp[i] += dp[i - one] % (10**9 + 7)
        return sum(dp[i] for i in range(low, high + 1)) % (10**9 + 7)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

noruta

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值