Medium
题目描述
给你一个长度为 n 的整数数组 nums 和一个二维数组 queries,其中 queries[i] = [li, ri, vali]。
每个 queries[i] 表示对 nums 执行以下操作:
- 将 nums 中范围 [li, ri] 内每个下标的值减少至多 vali。
- 每个下标减少的值可以独立选择。
零数组是所有元素都等于 0 的数组。
返回使 nums 变为零数组所需的最小非负整数 k,即按顺序处理前 k 个查询后 nums 变为零数组。如果不存在这样的 k,返回 -1。
示例 1:
输入:nums = [2,0,2], queries = [[0,2,1],[0,2,1],[1,1,3]]
输出:2
示例 2:
输入:nums = [4,3,2,1], queries = [[1,3,2],[0,2,1]]
输出:-1
提示:
- 1 <= nums.length <= 10^5
- 0 <= nums[i] <= 5 * 10^5
- 1 <= queries.length <= 10^5
- queries[i].length == 3
- 0 <= li <= ri < nums.length
- 1 <= vali <= 5
解题思路
这道题要求找到最小的查询数量 k,使得执行前 k 个查询后数组变为零数组。
核心思路:
二分查找答案:k 的取值范围是 [0, queries.length],具有单调性。如果使用 k 个查询能让数组变零,那么使用 k+1 个查询也一定能让数组变零。因此可以用二分查找找到最小的 k。
差分数组优化区间更新:对于给定的 k 个查询,需要高效地计算每个位置最多能减少多少。使用差分数组可以将区间更新操作从 O(n) 优化到 O(1),总体计算前缀和的时间复杂度为 O(n)。
检查可行性:对于前 k 个查询,计算每个位置能减少的最大值,然后检查是否足以让对应的 nums[i] 变为 0。
算法流程:
- 二分查找 k 的值
- 对于每个候选的 k,使用差分数组计算前 k 个查询能提供的最大减少量
- 检查每个位置的减少量是否不小于对应的 nums[i]
- 如果所有位置都满足条件,说明这个 k 可行,尝试更小的 k;否则需要更大的 k
时间复杂度:O(log(queries.length) × n),空间复杂度:O(n)。
代码实现
class Solution {
public:
int minZeroArray(vector<int>& nums, vector<vector<int>>& queries) {
int n = nums.size();
int m = queries.size();
auto canMakeZero = [&](int k) -> bool {
vector<int> diff(n + 1, 0);
// 使用差分数组处理前k个查询
for (int i = 0; i < k; i++) {
int l = queries[i][0], r = queries[i][1], val = queries[i][2];
diff[l] += val;
diff[r + 1] -= val;
}
// 计算每个位置能减少的最大值
int reduction = 0;
for (int i = 0; i < n; i++) {
reduction += diff[i];
if (reduction < nums[i]) {
return false;
}
}
return true;
};
int left = 0, right = m, result = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (canMakeZero(mid)) {
result = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return result;
}
};
class Solution:
def minZeroArray(self, nums: List[int], queries: List[List[int]]) -> int:
n = len(nums)
m = len(queries)
def can_make_zero(k):
diff = [0] * (n + 1)
# 使用差分数组处理前k个查询
for i in range(k):
l, r, val = queries[i]
diff[l] += val
diff[r + 1] -= val
# 计算每个位置能减少的最大值
reduction = 0
for i in range(n):
reduction += diff[i]
if reduction < nums[i]:
return False
return True
left, right = 0, m
result = -1
while left <= right:
mid = (left + right) // 2
if can_make_zero(mid):
result = mid
right = mid - 1
else:
left = mid + 1
return result
public class Solution {
public int MinZeroArray(int[] nums, int[][] queries) {
int n = nums.Length;
int m = queries.Length;
bool CanMakeZero(int k) {
int[] diff = new int[n + 1];
// 使用差分数组处理前k个查询
for (int i = 0; i < k; i++) {
int l = queries[i][0], r = queries[i][1], val = queries[i][2];
diff[l] += val;
diff[r + 1] -= val;
}
// 计算每个位置能减少的最大值
int reduction = 0;
for (int i = 0; i < n; i++) {
reduction += diff[i];
if (reduction < nums[i]) {
return false;
}
}
return true;
}
int left = 0, right = m, result = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (CanMakeZero(mid)) {
result = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return result;
}
}
var minZeroArray = function(nums, queries) {
const n = nums.length;
const m = queries.length;
const canMakeZero = (k) => {
const diff = new Array(n + 1).fill(0);
// 使用差分数组处理前k个查询
for (let i = 0; i < k; i++) {
const [l, r, val] = queries[i];
diff[l] += val;
diff[r + 1] -= val;
}
// 计算每个位置能减少的最大值
let reduction = 0;
for (let i = 0; i < n; i++) {
reduction += diff[i];
if (reduction < nums[i]) {
return false;
}
}
return true;
};
let left = 0, right = m, result = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (canMakeZero(mid)) {
result = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return result;
};
复杂度分析
| 项目 | 复杂度 |
|---|---|
| 时间复杂度 | O(log m × n),其中 m 是查询数量,n 是数组长度 |
| 空间复杂度 | O(n),用于存储差分数组 |
相关题目
. Corporate Flight Bookings (Medium)
. Zero Array Transformation IV (Medium)