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

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

最小化将所有数组元素减少到 0 所需的子数组增量减量

问题描述

给定一个整数数组 nums,每次操作将会选择任意一个 nums[i],将其变为 nums[i] - x(x 可为任意非负整数)。你最多可以执行 k 次操作。

需要找到让所有元素都等于 0 的最小 x 的值。

问题分析

这个问题本质上是一个二分搜索的问题,但是有一些需要注意的地方:

  1. x 可以是非负整数,因此我们需要考虑使用二分查找的范围
  2. 该问题可以转化为寻找所有 nums[i] - x 的和最小,因此需要先将整个数组全部转化成 nums[i] - x 的形式
  3. 另外一个问题就是如何计算子数组所要减去的增量和减量,可以使用前缀和的方式计算
详细实现

先将整个数组全部转化成 nums[i] - x 的形式,计算得到转化后的数组 new_nums。

然后针对每个元素 new_nums[i],计算其所对应的子数组的增量或者减量,具体的方法可以使用前缀和计算。

接下来可以使用二分查找的方式来找到最小的 x 值。

具体实现如下:

class Solution {
public:
    int minMoves(vector<int>& nums, int k) {
        vector<int> diff;
        vector<int> pre_sum(k, 0);
        for (int i=0, j=0; i<nums.size(); i++) {
            if (nums[i] == 1) {
                diff.push_back(i-j);
                pre_sum[j%k] += i-j;
                j++;
            }
        }
        for (int i=k-2; i>=0; i--) {
            pre_sum[i] += pre_sum[i+1];
        }
        int res = INT_MAX;
        for (int i=0; i<(diff.size()-k+1); i++) {
            int mid = i + k/2;
            int left_sum = pre_sum[i] - ((k/2)*(k/2+1))/2;
            int right_sum = pre_sum.back() - pre_sum[mid] - ((k/2)*(k/2-1))/2;
            res = min(res, left_sum + right_sum - diff[mid]);
        }
        return res;
    }
};

其中,diff 数组为新数组的差分数组,pre_sum 数组为其前缀和数组。算法的时间复杂度为 O(nlogn)。