📌  相关文章
📜  将所有数组元素减少到零所需的最小子数组减量(1)

📅  最后修改于: 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] 的时候需要格外小心。