📌  相关文章
📜  通过最多 K 次减少来最小化数组的总和(1)

📅  最后修改于: 2023-12-03 15:42:02.759000             🧑  作者: Mango

通过最多 K 次减少来最小化数组的总和

问题描述:给定一个整数数组 nums 和一个整数 k,你需要将 nums 中的元素进行 k 次以下操作:

  • 从 nums 中选择任意一个数字 x,使所有等于 x 的元素都变为 x-1。
  • 以下操作只能执行 k 次。

你需要最小化 nums 中所有元素的和。

示例:

输入: nums = [4,3,1], k = 2
输出: 5
解释:
操作如下:
- 把 nums[0] 减少到 2,数组变成 [2,3,1]。
- 把 nums[1] 减少到 2,数组变成 [2,2,1]。
解法

首先对数组进行排序,从最大值开始向下遍历每个数。对于每个数,我们考虑把从它到它前面的数都变成它本身,所需要减少的次数。如果这个次数大于 k,那么就停止遍历计算。

具体地,假设当前要考虑的数为 nums[i],nums[i-1] 变成 nums[i] 的代价为 i*(nums[i]-nums[i-1])。所以,我们从 i = n-1 开始倒序遍历 nums,对于每个 i,做如下操作:

  • 计算 nums[i] 到 nums[0] 中所有数变成 nums[i] 的代价 cost。
  • 如果 cost <= k,就将 nums[0] 到 nums[i] 中所有数都变成 nums[i],k -= cost。
  • 如果 cost > k,就将 nums[i] 变成 nums[i] - 1,因为只有这样才能保证后面的操作对结果的贡献最小。

最终,数组 nums 中所有元素的和就是我们要求的答案。

具体实现请见以下代码:

def min_sum_after_k_operations(nums: List[int], k: int) -> int:
    n = len(nums)
    nums.sort(reverse=True)
    ans = nums[0]
    for i in range(1, n):
        cost = i * (nums[i-1] - nums[i])
        if cost <= k:
            ans += i * (nums[i-1] + nums[i]) // 2 * (nums[i-1] - nums[i])
            k -= cost
        else:
            delta = k // i
            ans += i * (nums[i-1] + nums[i]) // 2 * (nums[i-1] - delta)
            ans += (nums[i-1] - delta) * (k % i)
            k -= k
        if k <= 0:
            break
    return ans
时间复杂度

代码中有一个排序操作,时间复杂度为 O(n log n),后面的遍历操作的时间复杂度为 O(n),因此总的时间复杂度为 O(n log n)。

空间复杂度

不需要额外的空间,空间复杂度为 O(1)。

参考文献