动态规划练习题①
70. 爬楼梯
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 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)表示爬到下标为n的台阶的方法数
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:
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. 统计构造好字符串的方案数
给你整数 zero
,one
,low
和 high
,我们从空字符串开始构造一个字符串,每一步执行下面操作中的一种:
- 将
'0'
在字符串末尾添加zero
次。 - 将
'1'
在字符串末尾添加one
次。
以上操作可以执行任意次。
如果通过以上过程得到一个 长度 在 low
和 high
之间(包含上下边界)的字符串,那么这个字符串我们称为 好 字符串。
请你返回满足以上要求的 不同 好字符串数目。由于答案可能很大,请将结果对 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:
"""
1. 递归
好字符串的数目分别为字符串长度为low,low+1...high-1,high的个数总和
在求每一个长度的字符串个数时,可以将此抽象为爬楼梯
假设字符串个数为target,我们要求爬到第target阶的方法数
s = [zero * "0", one * "1"]
def dfs(target):
if target == 0:
return 1
tol = 0
for i in s:
if len(i) <= target:
tol += dfs(target - len(i))
return tol
count = 0
for i in range(low, high + 1):
count += dfs(i)
return count % (10**9 + 7)
"""
"""
2. 记忆化搜索
s = [zero * "0", one * "1"]
def dfs(target):
if target == 0:
return 1
if cache[target] != -1:
return cache[target]
tol = 0
for i in s:
if len(i) <= target:
tol += dfs(target - len(i))
cache[target] = tol
return tol
count = 0
for i in range(low, high + 1):
cache = [-1] * (i + 1)
count += dfs(i)
return count % (10**9 + 7)
"""
"""
3. 递推
s = [zero * "0", one * "1"]
tol = 0
for i in range(low, high + 1):
dp = [0] * (i + 1)
dp[0] = 1
for j in range(1, i + 1):
for k in s:
if len(k) <= j:
dp[j] += dp[j - len(k)]
tol += dp[-1]
return tol % (10**9 + 7)
"""
# 上面的代码只能过30/36
# 递推版本二
# dp[i]为目标长度为i时,选择zero个0的方法个数 + 选择one个1的方法个数
dp = [0] * (high + 1)
dp[0] = 1
for i in range(1, high + 1):
if i >= zero:
dp[i] += dp[i - zero]
if i >= one:
dp[i] += dp[i - one]
return sum(dp[low:]) % (10**9 + 7)