70. 爬楼梯 (进阶)
本题难点在于如何映射到完全背包问题
- 什么是背包,什么是物品?
- 1阶,2阶,.... m阶就是物品,楼顶就是背包(容量)。
- 对应本题:m=2因为可以爬1step或者2steps,可以写成数组形式 temp = [i for i in range(m+1)]; 楼顶就是n(也就是#377. 组合总和 Ⅳ中的target)
注意每一阶可以重复使用,例如跳了1阶,还可以继续跳1阶(也就是上面的temp数组中的元素可以重复使用)。
问跳到楼顶有几种方法其实就是问装满背包有几种方法。
动规五部曲(也可以参考#377. 组合总和 Ⅳ)
- 确定dp数组以及下标的含义:dp[i]:爬到有i个台阶的楼顶,有dp[i]种方法。
- 确定递推公式:dp[i] = dp[i]+dp[i-temp[j]]
- dp数组如何初始化:dp[0] = 1
- 确定遍历顺序:注意先上1阶再上2阶和先上2阶再上1阶是不同的走法,因此是求排列问题,这样与#377. 组合总和 Ⅳ一样,要先背包后物品的遍历顺序
- 打印检查
class Solution(object):
def climbStairs(self, n):
"""
:type n: int
:rtype: int
"""
dp = [0]*(n+1)
dp[0] = 1
m = 2 #一次最多可以走2阶
temp = [i for i in range(m+1)]
for i in range(1, n+1): #先背包
for j in range(1, len(temp)): #后物品
if i>= temp[j]:
dp[i] = dp[i]+dp[i-temp[j]]
return dp[n]
322. 零钱兑换
这道题可以相对直观的对应到完全背包问题
与#518. 零钱兑换 II对比理解
动规五部曲:
- 确定dp数组以及下标的含义: dp[j]:凑足总额为j所需钱币的最少个数为dp[j]
- 递推公式:不考虑coins[i]的情况,那么凑足总额为j的最少个数为dp[j](step1定义); 考虑coins[i]的情况,那么首先凑足总额为j - coins[i]的最少个数为dp[j - coins[i]],然后只需要加上一个钱币coins[i]即dp[j - coins[i]] + 1就是dp[j]。由于要求最少个数,那么这两个取最小 --> dp[j] = min(dp[j], dp[j-coins[i]]+1)
- dp数组如何初始化:dp[amount+1)(amount+1)
- 确定遍历顺序:由于是求满足条件的最小个数(既不是求组合也不是求排列)
- 打印检查
class Solution(object):
def coinChange(self, coins, amount):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
dp = [10001]*(amount+1)
dp[0] = 0
for i in range(len(coins)):
for j in range(coins[i], amount+1):
dp[j] = min(dp[j], dp[j-coins[i]]+1)
if dp[amount] == 10001:
return -1
return dp[amount]
279.完全平方数
这道题跟#322. 零钱兑换非常相似,区别在于#322给定了coins数组,这道题需要自己先写出来要处理的数组temp。
根据题目要求,temp数组就是包含小于n的所有Perfect Squares。
e.g,n = 12, then temp = [1, 4, 9]
class Solution(object):
def numSquares(self, n):
"""
:type n: int
:rtype: int
"""
temp = []
for i in range(1, n):
if i**2 <= n:
temp.append(i**2)
# print(temp)
if n == 1:
return 1
dp = [10001]*(n+1)
dp[0] = 0
for i in range(len(temp)):
for j in range(temp[i], n+1):
dp[j] = min(dp[j], dp[j-temp[i]]+1)
return dp[n]