2025-09-16:零数组变换Ⅳ 用go语言,给定一个长度为 n 的整数数

B站影视 电影资讯 2025-09-16 15:48 7

摘要:2025-09-16:零数组变换Ⅳ。用go语言,给定一个长度为 n 的整数数组 nums 和若干查询 queries,其中每个查询用三元组 [li, ri, vali] 表示一次操作规则:

2025-09-16:零数组变换Ⅳ。用go语言,给定一个长度为 n 的整数数组 nums 和若干查询 queries,其中每个查询用三元组 [li, ri, vali] 表示一次操作规则:

• 对于该查询,你可以在下标区间 [li, ri] 里任选一些位置(也可以不选),把这些位置上的元素各自减去相同的数值 vali。

目标是按查询给出的顺序依次执行前 k 次操作(对于每次操作可以自由选择区间内的下标集合),使得最终数组中所有元素都变为 0。要求找出满足这一条件的最小非负整数 k;如果不存在任何 k 能达到这个目标,返回 -1。

1

0

1

queries[i] = [li, ri, vali]。

0

1

输入: 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。

给定一个整数数组 nums 和一系列查询 queries,每个查询是一个三元组 [li, ri, vali],表示可以在下标区间 [li, ri] 内任意选择一些位置(可以不选),将这些位置上的元素减去 vali。目标是按顺序执行前 k 次操作(每次操作可以自由选择区间内的下标集合),使得最终数组所有元素变为0。要求找出最小的非负整数 k(即使用前 k 个查询),如果无法实现则返回-1。

1. 问题分析:每个查询操作相当于提供了一种“资源”(即可以减去某个值 vali),但该资源只能应用于特定区间内的元素。对于每个非零元素 nums[i],我们需要通过一系列操作(即选择一些查询)来恰好减去 nums[i]。注意,每次操作(查询)可以自由选择区间内的下标,因此同一个操作可以被多个元素使用(但每次使用只能减去固定的 vali),但每个元素需要减去多个不同的 vali(来自不同的操作)来达到0。

2. 关键观察:对于每个位置i(其初始值为nums[i]),我们需要从查询集合中选出一个子集(这些查询必须覆盖位置i,即li

3. 二分搜索:我们使用二分搜索来寻找最小的k(0到len(queries)+1),使得前k个查询能够满足所有位置的需求(即对于每个位置i,存在一个覆盖它的查询子集(来自前k个查询),其 vali 的和等于 nums[i])。

4. 验证函数(二分搜索的内部):对于给定的k(即使用前k个查询),检查是否所有位置i(其中nums[i] != 0)都能被满足:然而,代码中使用了多重背包的二进制优化(但为什么?)因为实际上,对于同一个vali值,可能有多个查询(即多个物品具有相同的价值vali)。因此,我们可以将相同vali的物品分组,形成多重背包?但注意:每个查询是独立的(即使vali相同),所以实际上每个物品都是独立的(因为每个查询对应一个物品),所以应该是01背包?但代码中却使用了多重背包的二进制优化:它将相同vali值的查询分组(计数),然后使用二进制优化(将相同价值的物品分组,然后拆分成2的幂次个)来优化。这是因为对于同一个vali值,如果有多个查询,那么这些查询是相同的物品(价值相同),所以可以合并(但注意:这些查询是不同的操作,但对于位置i来说,它们的效果相同(都是减去vali),所以确实可以视为相同物品?但这里每个查询只能被使用一次(对于位置i来说),所以实际上就是多重背包(有多个相同价值的物品)?因此,对于每个vali值(从1到10),我们统计覆盖i的查询中vali等于该值的查询数量(即物品的数量)。然后,使用二进制优化将多重背包转化为01背包。具体地,对于位置i,我们有一个背包容量为nums[i],以及10种物品(vali从1到10),每种物品有cnt[v]个。我们需要判断是否可以从这些物品中选出一个子集,使得恰好装满背包(即价值和等于nums[i])。

• 对于每个位置i(nums[i] != 0),收集所有覆盖i的前k个查询(即查询的区间[li, ri]包含i,且查询的索引小于k)。

• 对于这些查询,每个查询提供一个价值为 vali 的“物品”,并且每个查询的数量是1(但注意:同一个查询可以被多个位置使用,所以对于单个位置i来说,每个查询只能使用一次)。因此,对于位置i,问题转化为:从这些查询(物品)中选出一个子集,使得子集的和恰好为nums[i]?但这里每个查询(物品)的“体积”是vali,并且每个物品只有一个(但注意:实际上同一个查询可以被多个位置使用,所以对于单个位置i的限制是每个查询最多用一次?但这里我们只考虑一个位置i,所以确实每个查询最多只能选一次(即要么在位置i上应用该操作,要么不应用)。所以是01背包?但注意:每个查询的vali可能相同,所以可能有多个查询具有相同的vali?因此,对于位置i,我们实际上有多个物品(每个覆盖i的查询就是一个物品),物品的价值是vali,且每个物品只有一个。所以这是一个01背包问题?但物品数量最多为k(即前k个查询中覆盖i的查询数量),而背包容量为nums[i](最大1000)。但k最大1000,nums[i]最大1000,所以直接01背包(O(ncapacity))是可行的?但这里n是覆盖i的查询数量,最多1000,容量1000,所以最多10^6次操作 per position?但总共有10个位置,所以总验证次数最多1010^6=10^7,在Go中可能可行。

5. 使用大整数(big.Int)表示背包状态:为了高效地进行背包DP,代码中使用了一个大整数f(看作一个bitset),其中第j位为1表示背包容量j可以被恰好装满。初始化f=1(即容量0可以被装满)。然后,对于每种价值v,我们将其物品数量进行二进制拆分(例如,有5个价值为v的物品,则拆成1个、2个、2个?二进制拆分:1,2,2确实可以表示0~5),然后每次加入一个“大物品”(大小为vk,其中k是拆分出的数量)。通过左移和OR操作来更新状态(即加入一个大小为vk的物品,相当于将当前状态左移v*k位然后OR)。最后检查f的第nums[i]位是否为1(即容量nums[i]能否被装满)。

6. 验证过程:对于每个非零元素nums[i],我们执行上述的多重背包(二进制优化)判断。如果所有位置都能被满足,则k可行;否则不可行。

7. 二分搜索范围:k从0到len(queries)(包括),所以二分搜索最多进行log2(1000)≈10次。每次验证需要处理所有非零位置(最多10个),每个位置处理最多10种物品(每种物品二进制拆分后最多log2(1000)≈10组),所以总验证次数为10(二分次数)*10(位置)*10(物品种类)*10(二进制拆分组数)≈10000次操作(但实际中背包状态用bitset操作,左移和OR操作很快)。

• 二分搜索:O(log(Q)),其中Q是查询数量(最多1000,所以二分大约10次)。

• 每次验证:遍历所有位置(最多10个),对于每个位置,收集覆盖它的查询(但这里没有显式遍历所有查询?实际上,代码中对于每个位置i,遍历前k个查询来统计每个vali的出现次数?这需要O(k) per position,所以总验证中收集查询需要O(10*k))。

• 然后对于每个位置,进行多重背包的二进制优化:物品种类为10(vali从1到10),每种物品二进制拆分成O(log(cnt))组(最多log(1000)≈10组),所以总组数最多100组?然后对于每组,进行bitset的左移和OR操作(bitset长度为nums[i]+1,最大1001,所以每次左移和OR操作的时间复杂度可以看作O(1)?因为bitset用大整数实现,但实际操作时间与位数有关,但这里位数只有1001,所以可以视为常数时间)。

• 因此,每次验证的总时间复杂度为O(10 * k + 10 * 100) = O(k)(因为k最大1000,所以每次验证大约10*1000 + 1000 = 11000次操作?)。

• 总时间复杂度:二分次数 * 每次验证时间 = O(log(Q) * (10 * k)) = O(10 * 1000 * 10) = 100000(最坏情况下)。

• 主要空间是存储nums和queries:O(n + Q) = O(10+1000)=1010。

• 验证过程中,对于每个位置,统计cnt数组(大小11)和背包状态(一个大整数,大约1001位,所以空间O(1)?因为固定大小)。

• 因此总额外空间复杂度为O(n + Q)。

package mainimport ( "fmt" "math/big" "sort")func minzeroArray(nums int, queries int)int { ans := sort.Search(len(queries)+1, func(mx int)bool { p := new(big.Int) next: for i, x := range nums { if x == 0 { continue } cnt := [11]int{} for _, q := range queries[:mx] { if q[0] 0; pow2 *= 2 { k := min(pow2, num) f.Or(f, p.Lsh(f, uint(v*k))) // 视作一个大小为 v*k 的物品 if f.Bit(x) > 0 { continue next } num -= k } } returnfalse } returntrue }) if ans # -*-coding:utf-8-*-def min_zero_array(nums, queries): def feasible(mx): # 判断使用前 mx 个查询能否把 nums 全部变成 0 for i, x in enumerate(nums): if x == 0: continue # 统计在前 mx 个查询中覆盖位置 i 的每个 vali 的次数 cnt = {} for q in queries[:mx]: l, r, v = q if l 0: k = pow2 if pow2 > x) & 1) != 0: reachable = True break num -= k pow2 > x) & 1): return False return True # 二分最小的 mx (0..len(queries)) lo, hi = 0, len(queries) ans = -1 while lo

我们相信Go语言和算法为普通开发者提供了困境的“面试利器”,并致力于分享全面的编程知识。在这里,您可以找到最新的Go语言教程、算法解析、提升面试竞争力的秘籍以及行业动态。

·

来源:伟泽教育

相关推荐