重点五步法:
1. dp数组以及下表含义是什么?
2. 如何初始化
3. 递推公式是什么
4. 遍历顺序
5. 打印dp数组
509. Fibonacci Number
class Solution:
def fib(self, n: int) -> int:
if n == 0:
return 0
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[-1]
70. Climbing Stairs
class Solution:
def climbStairs(self, n: int) -> int:
if n in (1, 2):
return n
dp_1, dp_2 = 1, 2
for i in range(3, n + 1):
new = dp_1 + dp_2
dp_1, dp_2 = dp_2, new
return new
746. Min Cost Climbing Stairs
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
n = len(cost)
if n <= 2:
return min(cost)
dp2 = 0
dp1 = 0
for i in range(2, n + 1):
new = min(dp1 + cost[i - 1], dp2 + cost[i - 2])
dp2, dp1 = dp1, new
return new
62. Unique Paths
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
n = len(cost)
if n <= 2:
return min(cost)
dp2 = 0
dp1 = 0
for i in range(2, n + 1):
new = min(dp1 + cost[i - 1], dp2 + cost[i - 2])
dp2, dp1 = dp1, new
return new
63. Unique Paths II
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [[1 for _ in range(m)] for _ in range(n)]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i - 1][j] * (1- obstacleGrid[i - 1][j]) + dp[i][j - 1] * (1 - obstacleGrid[i][j - 1])
return dp[m - 1][n - 1]
343. Integer Break
class Solution:
def integerBreak(self, n: int) -> int:
dp = [0] * (n + 1)
dp[2] = 1
for i in range(3, n + 1):
for j in range(1, i // 2 + 1):
dp[i] = max(dp[i],j * dp[i - j], j * (i-j))
print(dp)
return dp[-1]
96. Unique Binary Seacrch Trees
class Solution:
def numTrees(self, n: int) -> int:
if n in (1, 0):
return 1
dp = [0] * (n + 1)
dp[0], dp[1], dp[2] =1, 1, 2
for i in range(3, n + 1):
for j in range(1, i + 1):
dp[i] += dp[j - 1] * dp[i - j]
print(dp)
return dp[-1]
背包问题:
概述: 01背包 完全背包
01背包: n种物品, 每个物品只有一个, 每个物品有相应重量和价值, 给一个背包能装重量m, 最多能装多少价值
完全背包: n种物品, 每种物品无限个
多重背包: n种物品, 每种物品数量各不相同
dp数组如何定义?
1. 二维dp数组: dp[i][j]表示
0到i之间的物品任取 放进容量为j的背包里
递推公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]])
不放物品i: dp[i - 1][j]
放物品i: dp[i - 1][j - weight[i]] + value[i]
初始化:
j = 0时 dp[i][j] = 0 for all i
i = 0时 dp[i][j] = (j >= weight[0]) * value[0]
2. 一维dp数组:
直接覆盖当前层, 因为二维数组是取决于上一行, 滚动数组
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
初始化: dp = [0, ..., 0], 这样从后向前更新 递推公式中原dp[j]不影响结果
更新顺序必须后到前, 因为后面的更新依赖于前面的数值
检查一个list里是否存在一个和等于target 或者尽可能接近target的task可以作为背包问题考虑
416. Partition Equal Subset Sum
class Solution:
def canPartition(self, nums: List[int]) -> bool:
total = sum(nums)
if total % 2 == 1:
return False
# 想象成重量价值等价的01背包问题
# 重量 = 价值, 最大化价值 = 最大化重量 = 装满
target = int(total / 2)
dp = [0] * int(target + 1)
for i in range(len(nums)):
for j in range(target, nums[i] - 1, -1):
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
return dp[-1] == target
494. Target Sum
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
if sum(nums) < abs(target):
return 0
total = sum(nums) + target
if total % 2 == 1:
return 0
goal = total // 2
dp = [[0 for _ in range(goal + 1)] for _ in range(len(nums))]
dp[0][0] = 1
if nums[0] <= goal:
dp[0][nums[0]] += 1
for i in range(1, len(nums)):
for j in range(goal + 1):
if j >= nums[i]:
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]]
else:
dp[i][j] = dp[i - 1][j]
print(dp)
return dp[-1][-1]
474. Ones and Zeros
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
dp = [[0 for _ in range(n + 1)] for _ in range(m + 1)]
for item in strs:
x = item.count('0')
y = item.count('1')
for i in range(m, x - 1, -1):
for j in range(n, y - 1, -1):
dp[i][j] = max(dp[i][j], dp[i-x][j - y] + 1)
return dp[-1][-1]
完全背包理论基础
区别在于每个物品可以使用无数次
其实只需要正序遍历背包容量即可
先遍历物品 后遍历背包:组合
先遍历背包 后遍历物品:排列
牛逼!
518. Coin Change II
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0 for _ in range(amount + 1)]
dp[0] = 1
for c in coins:
for j in range(c, amount + 1):
dp[j] = dp[j] + dp[j - c]
# print(dp)
return dp[-1]
377. Combination Sum IV
class Solution:
def combinationSum4(self, nums: List[int], target: int) -> int:
dp = [0 for _ in range(target + 1)]
dp[0] = 1
for j in range(1, target + 1):
for n in nums:
if n <= j:
dp[j] = dp[j] + dp[j - n]
return dp[-1]
322. Coin Change
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
dp = [float('inf') for _ in range(amount + 1)]
dp[0] = 0
for c in coins:
for j in range(c, amount + 1):
dp[j] = min(dp[j], dp[j - c] + 1)
if dp[-1] == float('inf'):
return -1
return dp[-1]
279. Perfect Squares
class Solution:
def numSquares(self, n: int) -> int:
import math
items = [i ** 2 for i in range(1, math.floor(math.sqrt(n)) + 1)]
dp = [float('inf') for _ in range(n + 1)]
dp[0] = 0
for item in items:
for j in range(1, n + 1):
if j >= item:
dp[j] = min(dp[j], dp[j - item] + 1)
return dp[-1]
139. Word Break
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
dp = [False for _ in range(len(s) + 1)]
dp[0] = True
for j in range(1, len(s) + 1):
for word in wordDict:
if j >= len(word) and s[j - len(word): j] == word:
dp[j] = (dp[j] or dp[j - len(word)])
return dp[-1]