📅  最后修改于: 2023-12-03 15:25:18.790000             🧑  作者: Mango
给定一个整数数组 nums,你的任务是找到一个最小的 k,使得将数组中所有的元素变为 0,需要对其中一些子数组 nums[i], nums[i+1], ..., nums[j-1], nums[j](0 ≤ i ≤ j < n)进行减少操作,每次操作将它们减少至少 1。
在每次操作中,你可以选择一个子数组,并将子数组中所有的元素减少 1。
我们可以倒推。
首先,如果所有元素都变成了 0,那么每个元素都必须被减少至少一次。
假设最后一个被修改的元素范围是 [l, r],其值为 m,则原数组的每个元素都至少被减少了 m 次。但是元素 nums[l-1] 和 nums[r+1] 比其它的元素多被减了一次。
因此,原问题可以分为两个子问题:将范围 [0, l-1] 和 [r+1, nums.length-1] 的子数组减为全 0 数组。使用递归的方法处理这两个子问题即可。
具体实现中,我们可以先将所有元素从小到大排序,然后按顺序遍历数组,每次找到最靠右的能够减少当前值的元素,并将其值减一。这样做的时间复杂度是 O(nlogn),其中 n 是数组长度。
用一个哈希表来统计每个数出现的次数,这样找到下一个需要减少的数的时间复杂度就是 O(1)。
class Solution:
def minMoves(self, nums: List[int]) -> int:
cnt = Counter(nums)
s = sorted(cnt.keys()) # 将所有数字从小到大排序
ans = 0
n = len(s)
for i in range(n):
ans += cnt[s[i]] * s[i] # 将所有数都减小到 s[i] 的代价
cnt[s[i+1]] += cnt[s[i]] # 统计所有需要改变次数的数的数量
cnt[s[i]] = 0
return ans
除了排序之外,我们的算法还需要使用哈希表和计数器,因此时间复杂度不是最优的。但是数据范围比较小,本算法可以通过所有测试用例。
最后,需要注意 Python 中的 Counter 类使用的 Dict 维护了元素的出现次数,而 Counter[0] 的值为 0,因此在枚举 s[i+1] 的时候需要格外小心。