2915. 和为目标值的最长子序列的长度
给你一个下标从 0 开始的整数数组 nums 和一个整数 target 。
返回和为 target 的 nums 子序列中,子序列 长度的最大值 。如果不存在和为 target 的子序列,返回 -1 。
子序列 指的是从原数组中删除一些或者不删除任何元素后,剩余元素保持原来的顺序构成的数组。
示例 1:
输入:nums = [1,2,3,4,5], target = 9
输出:3
解释:总共有 3 个子序列的和为 9 :[4,5] ,[1,3,5] 和 [2,3,4] 。最长的子序列是 [1,3,5] 和 [2,3,4] 。所以答案为 3 。
示例 2:
输入:nums = [4,1,3,2,1,5], target = 7
输出:4
解释:总共有 5 个子序列的和为 7 :[4,3] ,[4,1,2] ,[4,2,1] ,[1,1,5] 和 [1,3,2,1] 。最长子序列为 [1,3,2,1] 。所以答案为 4 。
示例 3:
输入:nums = [1,1,5,4,5], target = 3
输出:-1
解释:无法得到和为 3 的子序列。
提示:
1 <= nums.length <= 1000
1 <= nums[i] <= 1000
1 <= target <= 1000
# 1、记忆化搜索 + 递归
# 内存会超出限制
dfs(i,t) 定义为 序列长度为i,目标值为t时,最长子序列的长度
dfs(i,t) = max(dfs(i-1,t),dfs(i-1,t-nums[i]) + 1) 对于第i个元素,我们可以选,也可以不选
选第i个元素 最长子序列长度为 dfs(i-1,t-nums[i]) + 1
不选第i个元素 最长子序列长度为 dfs(i-1,t)
在二者中选最大值
def lengthOfLongestSubsequence(nums,target):
n = len(nums)
# 缓存计算过的结果
@cache
def dfs(i,t):
if i < 0:
return 0 if t == 0 else -inf
if t < nums[i]:
return dfs(i-1,t)
else:
return max(dfs(i-1,t),dfs(i-1,t-nums[i]) + 1)
ans = dfs(n-1,target)
return ans if ans > 0 else -1
# 2、递推
# 一般来说 只要可以递归的算法都可以递推
构建二维dp表时,通常行和列都各要多一行,这样是为了方便递推
二维表格起始全部赋值 -inf,对于 f[i][t], 只有t=0时,才算找到了所有的值的和等于目标值
当i=0时,表示递推的起始位置,所以赋值f[0][0] = 0,t != 0的其他位置都不满足条件为-inf
f[i][t] 表示序列长度为i,目标值为t时,等于目标和的最长子集序列长度
def lengthOfLongestSubsequence(nums,target):
n = len(nums)
f = [[-inf]*(target+1) for _ in range(n+1)]
f[0][0] = 0
for i,x in enumerate(nums):
for t in range(target+1):
if x > t:
f[i+1][t] = f[i][t]
else:
f[i+1][t] = max(f[i][t],f[i][t-x]+1)
ans = f[n][target]
return ans if ans > 0 else -1
# 3、 优化递推
# 动态规划的无后效性,动态规划只与上一个状态有关,所以我们可以原地更新序列,减少空间复杂度
注意这个地方遍历目标值的时候要倒序,因为我们计算新一轮的值只能用上一轮的值
所以如果用正序,如果我们计算f[5]时,可能会用到f[2],而用正序时我们前面已经计算过f[2],所以计算结果就会用这一轮的f[2]就会报错
如果倒序,我们就只会用到上一轮的值
def lengthOfLongestSubsequence(nums,target):
n = len(nums)
f = [-inf]*(target+1)
f[0] = 0
for i,x in enumerate(nums):
for t in range(target,-1,-1):
if x > t:
f[t] = f[t]
else:
f[t] = max(f[t],f[t-x]+1)
ans =f[target]
return ans if ans > 0 else -1
416. 分割等和子集
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 100
# 记忆化搜索 + 递归
def canPartition(nums):
s = sum(nums)
if s % 2 > 0:
return False
t = s / 2
@cache
def dfs(i,x):
if i < 0:
return True if x == 0 else False
if x < nums[i]:
return dfs(i-1,x)
else:
return dfs(i-1,x) or dfs(i-1,x-nums[i])
return dfs(len(nums)-1,t)
# 递推
def canPartition(nums):
s = sum(nums)
m = len(nums)
if s % 2 > 0:
return False
# 不加int 会报错
t = int(s / 2)
f = [[False]*(t+1) for _ in range(m+1)]
f[0][0] = True
for i,x in enumerate(nums):
for c in range(t+1):
if x > c:
f[i+1][c] = f[i][c]
else:
f[i+1][c] = f[i][c] or f[i][c -x]
return f[m][t]
# 优化后的递推
def canPartition(nums):
s = sum(nums)
m = len(nums)
if s % 2 > 0:
return False
# 不加int 会报错
t = int(s / 2)
f = [False]*(t+1)
f[0] = True
for i,x in enumerate(nums):
for c in range(t,-1,-1):
if x > c:
f[c] =f[c]
else:
f[c] = f[c] or f[c -x]
return f[t]
494. 目标和
给你一个非负整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
示例 1:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
示例 2:
输入:nums = [1], target = 1
输出:1
提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000
数组的和为s 加 '-'号的原本的和为p 目标和为target
所以有 (s-p)-p = target, 2p = s -target ==> p = (s-target ) /2
所以我们只需要在nums中找目标和等于p的组合种数
def findTargetSumWays(self, nums, target):
s = sum(nums)
if s < target:
return 0
if (s-target) % 2 == 1:
return 0
p = (s-target)/2
@cache
def dfs(i,x):
if i < 0:
return 1 if x == 0 else 0
if x < nums[i]:
return dfs(i-1,x)
else:
return dfs(i-1,x) + dfs(i-1,x-nums[i])
return dfs(len(nums)-1, p)
# 递推
def findTargetSumWays(self, nums, target):
s = sum(nums)
n = len(nums)
if s < target:
return 0
if (s-target) % 2 == 1:
return 0
p = int((s-target)/2)
f = [[0]*(p+1) for _ in range(n+1)]
f[0][0] = 1
for i,x in enumerate(nums):
for c in range(p+1):
if x > c:
f[i+1][c] = f[i][c]
else:
f[i+1][c] = f[i][c] + f[i][c-x]
return f[n][p]
# 优化后的递推
def findTargetSumWays(self, nums, target):
s = sum(nums)
n = len(nums)
if s < target:
return 0
if (s-target) % 2 == 1:
return 0
p = int((s-target)/2)
f = [0]*(p+1)
f[0] = 1
for i,x in enumerate(nums):
for c in range(p, -1, -1):
if x > c:
f[c] = f[c]
else:
f[c] = f[c] + f[c-x]
return f[p]
2787. 将一个数字表给你两个 正 整数 n 和 x 。
请你返回将 n 表示成一些 互不相同 正整数的 x 次幂之和的方案数。换句话说,你需要返回互不相同整数 [n1, n2, …, nk] 的集合数目,满足 n = n1**x + n2**x + … + nk**x 。由于答案可能非常大,请你将它对 10**9 + 7 取余后返回。
比方说,n = 160 且 x = 3 ,一个表示 n 的方法是 n = 2**3 + 3**3 + 5**3 。示成幂的和的方案数
示例 1:
输入:n = 10, x = 2
输出:1
解释:我们可以将 n 表示为:n = 3**2 + 1**2 = 10 。
这是唯一将 10 表达成不同整数 2 次方之和的方案。
示例 2:
输入:n = 4, x = 1
输出:2
解释:我们可以将 n 按以下方案表示:
- n = 4**1 = 4 。
- n = 3**1 + 1**1 = 4 。
提示:
1 <= n <= 300
1 <= x <= 5
# 记忆化搜索 + 递归
注意这个地方是小于1,不是小于0,因为是求正整数组合,所以小于1时就迭代完了
def numberOfWays(n, x):
@cache
def dfs(i,t):
if i < 1:
return 1 if t == 0 else 0
if t < i**x:
return dfs(i-1,t) % (10**9 + 7)
else:
return (dfs(i-1,t) + dfs(i-1,t-i**x)) % (10**9+7)
return dfs(n,n)
# 递推
def numberOfWays(n, x):
f = [[0]*(n+1) for _ in range(n+1)]
f[0][0] = 1
for i in range(1,n+1):
for c in range(n+1):
if c < i**x:
f[i][c] = f[i-1][c]
else:
f[i][c] = f[i-1][c] + f[i-1][c - i**x]
ans = f[n][n] % (10**9 + 7)
return ans
# 优化后的递推
def numberOfWays(n, x):
f = [0]*(n+1)
f[0] = 1
for i in range(1,n+1):
for c in range(n,-1,-1):
if c < i**x:
f[c] = f[c]
else:
f[c] = f[c] + f[c - i**x]
ans = f[n] % (10**9 + 7)
return ans
3180. 执行操作可获得的最大总奖励 I
给你一个整数数组 rewardValues,长度为 n,代表奖励的值。
最初,你的总奖励 x 为 0,所有下标都是 未标记 的。你可以执行以下操作 任意次 :从区间 [0, n - 1] 中选择一个 未标记 的下标 i。
如果 rewardValues[i] 大于 你当前的总奖励 x,则将 rewardValues[i] 加到 x 上(即 x = x + rewardValues[i]),并 标记 下标 i。
以整数形式返回执行最优操作能够获得的 最大 总奖励
示例 1:
输入:rewardValues = [1,1,3,3]
输出:4
解释:
依次标记下标 0 和 2,总奖励为 4,这是可获得的最大值。
示例 2:
输入:rewardValues = [1,6,4,3,2]
输出:11
解释:
依次标记下标 0、2 和 1。总奖励为 11,这是可获得的最大值。
提示:
1 <= rewardValues.length <= 2000
1 <= rewardValues[i] <= 2000
# 记忆化搜索 + 递归
# 超出内存限制 ,这个地方 rewardValues 倒序排序,不然会得到错误的结果
def maxTotalReward(self, rewardValues: List[int]) -> int:
rewardValues.sort(reverse=True)
@cache
def dfs(i,x):
if i < 0:
return x
if rewardValues[i] <= x:
return dfs(i-1,x)
else:
return max(dfs(i-1,x),dfs(i-1,x+rewardValues[i]))
return dfs(len(rewardValues) - 1,0)
474. 一和零
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集
示例 1:
输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。
示例 2:
输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2 。
提示:
1 <= strs.length <= 600
1 <= strs[i].length <= 100
strs[i] 仅由 '0' 和 '1' 组成
1 <= m, n <= 100
# 记忆化搜索 + 递归
def findMaxForm(strs: List[str], m: int, n: int) -> int:
@cache
def dfs(i,m,n):
x = strs[i]
if i < 0:
return 0 if m >=0 and n >=0 else -inf
if x.count('0') > m or x.count('1') > n:
return dfs(i-1,m,n)
else:
return max(dfs(i-1,m,n),dfs(i-1, m-x.count('0'), n-x.count('1')) + 1)
return dfs(len(strs)-1,m,n)