大家好,我是晴天学长,很久很久没有写算法题解了,今天开始转python了。💪💪💪
1)统计打字方案数
给你一个长度为 n 的整数数组 nums 和一个二维数组 queries ,其中 queries[i] = [li, ri, vali]。
每个 queries[i] 表示以下操作在 nums 上执行:
从数组 nums 中选择范围 [li, ri] 内的一个下标子集。 将每个选中下标处的值减去 正好 vali。
零数组 是指所有元素都等于 0 的数组。
返回使得经过前 k 个查询(按顺序执行)后,nums 转变为 零数组 的最小可能 非负 值 k。如果不存在这样的 k,返回 -1。
数组的 子集 是指从数组中选择的一些元素(可能为空)。
示例 1:
输入: nums = [2,0,2], queries = [[0,2,1],[0,2,1],[1,1,3]]
输出: 2
解释:
对于查询 0 (l = 0, r = 2, val = 1):
将下标 [0, 2] 的值减 1。
数组变为 [1, 0, 1]。
对于查询 1 (l = 0, r = 2, val = 1):
将下标 [0, 2] 的值减 1。
数组变为 [0, 0, 0],这就是一个零数组。因此,最小的 k 值为 2。
示例 2:
输入: nums = [4,3,2,1], queries = [[1,3,2],[0,2,1]]
输出: -1
解释:
即使执行完所有查询,也无法使 nums 变为零数组。
示例 3:
输入: nums = [1,2,3,2,1], queries = [[0,1,1],[1,2,1],[2,3,2],[3,4,1],[4,4,1]]
输出: 4
解释:
对于查询 0 (l = 0, r = 1, val = 1):
将下标 [0, 1] 的值减 1。
数组变为 [0, 1, 3, 2, 1]。
对于查询 1 (l = 1, r = 2, val = 1):
将下标 [1, 2] 的值减 1。
数组变为 [0, 0, 2, 2, 1]。
对于查询 2 (l = 2, r = 3, val = 2):
将下标 [2, 3] 的值减 2。
数组变为 [0, 0, 0, 0, 1]。
对于查询 3 (l = 3, r = 4, val = 1):
将下标 4 的值减 1。
数组变为 [0, 0, 0, 0, 0]。因此,最小的 k 值为 4。
示例 4:
输入: nums = [1,2,3,2,6], queries = [[0,1,1],[0,2,1],[1,4,2],[4,4,4],[3,4,1],[4,4,5]]
输出: 4
提示:
1 <= nums.length <= 10
0 <= nums[i] <= 1000
1 <= queries.length <= 1000
queries[i] = [li, ri, vali]
0 <= li <= ri < nums.length
1 <= vali <= 10
2) .算法思路
我们首先注意到nums长度最长才只有10,我们可以枚举每一个询问,处理出一个arr[i],arr[i]也是一个数组,代表了的位置i有哪些询问可以使用,并且是有序的。然后对于每一个询问可以处理的位置,加到对应的位置的数组中即可。
然后对于每一个位置,就是判断选择这些询问,是否能够凑出这个位置的值,并且是在用到的询问最小的情况下,那么这就是01背包,对每一个位置我们都求一个01背包,定义f[i][j]代表了前i个询问是否能够凑出j。然后对于每一个位置求出的最少询问数求最大值。
需要注意的是,这道题如果num所有元素为0的时候,是直接返回0,需要判断一下,有点坑。
3) .算法步骤
1.创建一个二维数组arr,用于记录每个点能够被哪些区间覆盖。对于每个查询,遍历区间中的点,将该点与对应的查询值存储在arr中。
2.对于每个点,遍历其覆盖的区间,使用动态规划来尝试凑出该点的值。定义一个二维数组f,其中f[i][j]表示在考虑前i个区间的情况下,是否可以凑出值为j。
3.初始化f[0][0] = True,表示不选取任何区间时,可以凑出值为0。
4.对于每个区间,考虑选取或不选取该区间。如果选取该区间,则需要判断是否可以凑出值为j - arr[k][i][1]。
5.如果最终在考虑完所有区间后,可以凑出该点的值,则记录下使用的区间索引,并更新cur的值。
6.最终返回mx + 1,其中mx表示使用的区间索引的最大值。
4).代码示例
class Solution:def minZeroArray(self, nums: List[int], queries: List[List[int]]) -> int:if max(nums) == 0:return 0n = len(nums)m = len(queries)arr = [[] for _ in range(n)]#每个点能够被哪一些区间覆盖for i in range(m):l, r, val = queries[i]for j in range(l, r + 1):arr[j].append((i, val))#对于每一个点,问题转换为了在尽量少用的情况下,凑出nums[i]mx = -inffor k,x in enumerate(nums):f = [[False] * (x + 1) for _ in range(len(arr[k]) + 1)]f[0][0] = Truecur = inffor i in range(len(arr[k])):for j in range(x + 1):#不选f[i + 1][j] = f[i][j]#选if j - arr[k][i][1] >= 0:f[i + 1][j] |= f[i][j - arr[k][i][1]] if f[i + 1][x]:cur = min(cur,arr[k][i][0])breakelse:return -1mx = max(mx,cur)return mx + 1
5).总结
这个算法主要是通过动态规划来解决问题。首先,对于每个点,算法找出它能够被哪些区间覆盖。然后,问题转化为对于每个点,如何在尽量少的情况下凑出该点的值。
试题链接