0-1背包 完全背包【基础算法精讲 18】_哔哩哔哩_bilibili
494. 目标和
给你一个整数数组 nums
和一个整数 target
。
向数组中的每个整数前添加 '+'
或 '-'
,然后串联起所有整数,可以构造一个 表达式 :
- 例如,
nums = [2, 1]
,可以在2
之前添加'+'
,在1
之前添加'-'
,然后串联起来得到表达式"+2-1"
。
返回可以通过上述方法构造的、运算结果等于 target
的不同 表达式 的数目。
假设所有添加正号的数字的和为p,那么所有添加负号的数字和为s-p,s是所有数字和。那么p - ( s - p ) = t,则2p = s + t,p=(s+t)/ 2
一 · 递归搜索 + 保存计算结果 = 记忆化搜索
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
target += sum(nums)
# 在相加的过程中只能是偶数加上偶数、奇数加上奇数才能得到偶数。所以如果 target 是负数或者奇数,肯定不存在符合条件的解,直接返回 0 即可。
if target < 0 or target % 2:
return 0
target //= 2
@cache # 记忆化搜索
def dfs(i, c):
if i < 0:
return 1 if c == 0 else 0
if c < nums[i]:
return dfs(i - 1, c)
return dfs(i - 1, c) + dfs(i - 1, c - nums[i])
return dfs(len(nums) - 1, target)
二 · 1:1 翻译成递推
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
target += sum(nums)
if target < 0 or target % 2:
return 0
target //= 2
n = len(nums)
f = [[0] * (target + 1) for _ in range(n + 1)]
f[0][0] = 1
for i, x in enumerate(nums):
for c in range(target + 1):
if c < x:
f[i + 1][c] = f[i][c]
else:
f[i + 1][c] = f[i][c] + f[i][c - x]
return f[n][target]
三 · 空间优化:两个数组(滚动数组)
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
target += sum(nums)
if target < 0 or target % 2:
return 0
target //= 2
n = len(nums)
f = [[0] * (target + 1) for _ in range(2)]
f[0][0] = 1
for i, x in enumerate(nums):
for c in range(target + 1):
if c < x:
f[(i + 1) % 2][c] = f[i % 2][c]
else:
f[(i + 1) % 2][c] = f[i % 2][c] + f[i % 2][c - x]
return f[n % 2][target]
四 · 空间优化:一个数组
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
target += sum(nums)
if target < 0 or target % 2:
return 0
target //= 2
f = [1] + [0] * target
for x in nums:
for c in range(target, x - 1, -1):
f[c] += f[c - x]
return f[target]
322. 零钱兑换
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
一 · 递归搜索 + 保存计算结果 = 记忆化搜索
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
@cache
def dfs(i, c):
if i < 0:
return 0 if c == 0 else inf
if c < coins[i]:
return dfs(i - 1, c)
return min(dfs(i - 1, c), dfs(i, c - coins[i]) + 1)
ans = dfs(len(coins) - 1, amount)
return ans if ans < inf else -1
![](https://i-blog.csdnimg.cn/blog_migrate/396b3b67f1ad3ddb5d20ee87a6607a10.png)
二 · 1:1 翻译成递推
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
n = len(coins)
f = [[inf] * (amount + 1) for _ in range(n + 1)]
f[0][0] = 0
for i, x in enumerate(coins):
for c in range(amount + 1):
if c < x:
f[i + 1][c] = f[i][c]
else:
f[i + 1][c] = min(f[i][c], f[i + 1][c - x] + 1)
ans = f[n][amount]
return ans if ans < inf else -1
三 · 空间优化:两个数组(滚动数组)
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
n = len(coins)
f = [[inf] * (amount + 1) for _ in range(2)]
f[0][0] = 0
for i, x in enumerate(coins):
for c in range(amount + 1):
if c < x:
f[(i + 1) % 2][c] = f[i % 2][c]
else:
f[(i + 1) % 2][c] = min(f[i % 2][c], f[(i + 1) % 2][c - x] + 1)
ans = f[n % 2][amount]
return ans if ans < inf else -1
四 · 空间优化:一个数组
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
f = [0] + [inf] * amount
for x in coins:
for c in range(x, amount + 1):
f[c] = min(f[c], f[c - x] + 1)
ans = f[amount]
return ans if ans < inf else -1