1. :分割等和子集
题目链接: 416. 分割等和子集 - 力扣(LeetCode)
应用条件:动态规划
难点:
# 转化为01背包问题,j为sum(nums)//2,看在数组中能否取出来总和的一半,能就True,不能就False
# 确定dp数组(dp table)以及下标的含义:dp[i][j]表示在容量为j的背包中i个物品可以取得的最大价值
# 确定递推公式: 如果nums[i]>j: dp[i][j] = dp[i-1][j],else:不放进去dp[i][j]=max(dp[i-1][j],放进去dp[i-1][j-nums[i]]+nums[i])
# dp数组如何初始化: dp[0][j]都是1,dp[j][0]中小于nums[0]为0否则为nums[0]
# 确定遍历顺序: i从1到len(nums) j 从1到sum(nums)//2
个人错误:
一定要想到,能分成两个子集可以转化成找一个和为sum(nums)//2 的子集
思路:
class Solution:
def canPartition(self, nums: List[int]) -> bool:
summ = sum(nums)
if summ % 2 != 0:
return False
target = summ // 2
dp = [[0]*(target+1) for i in range(len(nums))]
for j in range(nums[0],target+1):
dp[0][j] = nums[0]
for i in range(1,len(nums)):
for j in range(1,target+1):
if nums[i] > j:
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i])
if dp[len(nums)-1][-1] == target:
return True
else:
return False
2. :最后一块石头的重量II
题目链接: 1049. 最后一块石头的重量 II - 力扣(LeetCode)
应用条件:动态规划
难点:
# 转化为01背包问题,j为sum(stones)//2,看在数组中能否取出来总和的一半,能就返回0,不能就返回剩下的减去这个最接近一半的值
# 确定dp数组(dp table)以及下标的含义:dp[i][j]表示在容量为j的背包中i个物品可以取得的最大价值
# 确定递推公式: 如果stones[i]>j: dp[i][j] = dp[i-1][j],else:不放进去dp[i][j]=max(dp[i-1][j],放进去dp[i-1][j-stones[i]]+stones[i])
# dp数组如何初始化: dp[0][j]都是1,dp[j][0]中小于stones[0]为0否则为stones[0]
# 确定遍历顺序: i从1到len(stones) j 从1到sum(stones)//2
个人错误:
也是要想到可以转化为01背包问题,找到最能接近石头总重量一半的石头群,然后总重量-这个最大值-这个最大值就OK了,不用管石头总重量能否整除2,不影响。
思路:
class Solution:
def lastStoneWeightII(self, stones: List[int]) -> int:
if len(stones) == 0:
return 0
if len(stones) == 1:
return stones[0]
target = sum(stones)//2
dp = [[0]*(target+1) for i in range(len(stones))]
for j in range(stones[0],target+1):
dp[0][j] = stones[0]
for i in range(1,len(stones)):
for j in range(1,target+1):
if stones[i] > j:
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-stones[i]]+stones[i])
return sum(stones)-dp[len(stones)-1][-1]-dp[len(stones)-1][-1]
3. :目标和
题目链接: 494. 目标和 - 力扣(LeetCode)
应用条件:
难点:
# 本题要如何使表达式结果为target,既然为target,那么就一定有 left组合 - right组合 = target。left + right = sum,而sum是固定的。right = sum - left 公式来了, left - (sum - left) = target 推导出 left = (target + sum)/2 。target是固定的,sum是固定的,left就可以求出来。
# 转化为01背包问题,用一维数组来解决,dp[j]表示容量问j的背包,要达到j能有多少种放法,j为(target + sum)/2,不用担心有余数情况,有余数说明不能达到target
# 确定dp数组(dp table)以及下标的含义:dp[j]表示容量问j的背包,能有多少种放法
# 确定递推公式: for i in range(j):dp[j] += dp[j-nums[i]]
# dp数组如何初始化: 因为要累加dp[0] = 1
# 确定遍历顺序: i放外循环从0到len(nums) j放内循环从(target + sum)/2到nums[i] 倒着循环
个人错误:
要想到left = (target + sum)/2
思路:
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
if abs(target) > sum(nums):
return 0
if len(nums) == 0 or (target+sum(nums)) %2 != 0:
return 0
curtarget = (target+sum(nums))//2
dp = [0]*(curtarget+1)
dp[0] = 1
for i in range(len(nums)):
for j in range(curtarget,nums[i]-1,-1):
dp[j] += dp[j-nums[i]]
return dp[curtarget]
4. :一和零
题目链接: 474. 一和零 - 力扣(LeetCode)
应用条件:
难点:
# 转化为01背包问题,dp[i][j]表示背包可以放下i个0和j个1,其中放物品的最大数量
# 确定dp数组(dp table)以及下标的含义:dp[i][j]表示背包可以放下i个0和j个1,其中放物品的最大数量
# 确定递推公式: dp[i][j]=max(dp[i-x][j-y]+1,dp[i][j]) x,y表示这个物品x个0,y个1
# dp数组如何初始化: 因为是所放物品数量dp[0] = 0
# 确定遍历顺序: s放外循环从0到len(strs) i放内循环从m到x 倒着循环 j放内循环从n到y 倒着循环
个人错误:
思路:
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
dp = [[0]*(n+1) for i in range(m+1)]
for s in strs:
y = s.count('1')
x = s.count('0')
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[m][n]
5. :完全背包
题目链接:
应用条件:
难点:
完全背包就物品数量无限,一个物品可以放不限次,在01背包一维数组的方法中我们内循环是让j从最大bagsize倒叙向当前i物品的重量wight【i】循环,为的就是防止一个物品被放进去多次,那么对于完全背包,我们可以直接将j正序循环即可实现无限次放物品。
个人错误:
思路:
模板
def test_CompletePack(weight, value, bagWeight):
dp = [0] * (bagWeight + 1)
for i in range(len(weight)): # 遍历物品
for j in range(weight[i], bagWeight + 1): # 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
return dp[bagWeight]
6. :零钱兑换II
题目链接: 518. 零钱兑换 II - 力扣(LeetCode)
应用条件:
难点:
# 确定dp数组(dp table)以及下标的含义:dp[j]表示金额为amount的硬币组合数
# 确定递推公式: dp[j] += dp[j-coins[i]]
# dp数组如何初始化: dp[0]=1
# 确定遍历顺序: i从0到len(coins) j 从coins[i]到amount
个人错误:
把 j从0开始遍历了,不能从0开始要从coins[i]开始。
思路:
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp=[0]*(amount+1)
dp[0] =1
for i in range(len(coins)):
for j in range(coins[i],amount+1):
dp[j] += dp[j-coins[i]]
print(dp)
return dp[amount]
7. :组合总和 Ⅳ
题目链接:
应用条件:
难点:
# 确定dp数组(dp table)以及下标的含义:dp[j]表示目标target能有多少组合
# 确定递推公式: dp[j] += dp[j-nums[i]]
# dp数组如何初始化: dp[0]=1
# 确定遍历顺序: 排列先背包再物品 j 从0到target i从0到len(nums)
个人错误:
没有在遍历j的时候加判断条件,j - nums[i] >= 0的情况下才对dp数组进行操作
思路:
class Solution:
def combinationSum4(self, nums: List[int], target: int) -> int:
dp = [0]*(target+1)
dp[0] = 1
for j in range(1,target+1):
for i in range(len(nums)):
if j - nums[i] >=0:
dp[j] += dp[j-nums[i]]
return dp[target]