跳转至

3422. 将子数组元素变为相等所需的最小操作数 🔒

题目描述

给定一个整数数组 nums 和一个整数 k。你可以进行任意次以下操作:

  • 给 nums 的任何元素增加或减少 1。

返回确保 至少 有一个大小为 k 的 nums 中的 子数组 的所有元素都相等的所需的 最小 操作数。

 

示例 1:

输入:nums = [4,-3,2,1,-4,6], k = 3

输出:5

解释:

  • 使用 4 次操作来给 nums[1] 增加 4。结果数组为 [4, 1, 2, 1, -4, 6]
  • 使用 1 次操作来给 nums[2] 减少 1。结果数组为 [4, 1, 1, 1, -4, 6]
  • 现在数组包含一个大小为 k = 3 的子数组 [1, 1, 1],所有元素都想等。因此,答案为 5。

示例 2:

输入:nums = [-2,-2,3,1,4], k = 2

输出:0

解释:

  • 大小为 k = 2 的子数组 [-2, -2] 已经包含了所有相等的元素,所以不需要操作。因此答案为 0。

 

提示:

  • 2 <= nums.length <= 105
  • -106 <= nums[i] <= 106
  • 2 <= k <= nums.length

解法

方法一:有序集合

根据题目描述,我们需要找到一个长度为 \(k\) 的子数组,通过最少的操作使得子数组中的所有元素相等,即我们需要找到一个长度为 \(k\) 的子数组,使得子数组中所有元素变成这 \(k\) 个元素的中位数所需的操作次数最小。

我们可以使用两个有序集合 \(l\)\(r\) 分别维护 \(k\) 个元素的左右两部分,其中 \(l\) 用于存储 \(k\) 个元素中较小的一部分,\(r\) 用于存储 \(k\) 个元素中较大的一部分,并且 \(l\) 的元素个数要么等于 \(r\) 的元素个数,要么比 \(r\) 的元素个数少一个,这样 \(r\) 的最小值就是 \(k\) 个元素中的中位数。

时间复杂度 \(O(n \times \log k)\),空间复杂度 \(O(k)\)。其中 \(n\) 为数组 \(\textit{nums}\) 的长度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution:
    def minOperations(self, nums: List[int], k: int) -> int:
        l = SortedList()
        r = SortedList()
        s1 = s2 = 0
        ans = inf
        for i, x in enumerate(nums):
            l.add(x)
            s1 += x
            y = l.pop()
            s1 -= y
            r.add(y)
            s2 += y
            if len(r) - len(l) > 1:
                y = r.pop(0)
                s2 -= y
                l.add(y)
                s1 += y
            if i >= k - 1:
                ans = min(ans, s2 - r[0] * len(r) + r[0] * len(l) - s1)
                j = i - k + 1
                if nums[j] in r:
                    r.remove(nums[j])
                    s2 -= nums[j]
                else:
                    l.remove(nums[j])
                    s1 -= nums[j]
        return ans
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class Solution {
    public long minOperations(int[] nums, int k) {
        TreeMap<Integer, Integer> l = new TreeMap<>();
        TreeMap<Integer, Integer> r = new TreeMap<>();
        long s1 = 0, s2 = 0;
        int sz1 = 0, sz2 = 0;
        long ans = Long.MAX_VALUE;
        for (int i = 0; i < nums.length; ++i) {
            l.merge(nums[i], 1, Integer::sum);
            s1 += nums[i];
            ++sz1;
            int y = l.lastKey();
            if (l.merge(y, -1, Integer::sum) == 0) {
                l.remove(y);
            }
            s1 -= y;
            --sz1;
            r.merge(y, 1, Integer::sum);
            s2 += y;
            ++sz2;
            if (sz2 - sz1 > 1) {
                y = r.firstKey();
                if (r.merge(y, -1, Integer::sum) == 0) {
                    r.remove(y);
                }
                s2 -= y;
                --sz2;
                l.merge(y, 1, Integer::sum);
                s1 += y;
                ++sz1;
            }
            if (i >= k - 1) {
                ans = Math.min(ans, s2 - r.firstKey() * sz2 + r.firstKey() * sz1 - s1);
                int j = i - k + 1;
                if (r.containsKey(nums[j])) {
                    if (r.merge(nums[j], -1, Integer::sum) == 0) {
                        r.remove(nums[j]);
                    }
                    s2 -= nums[j];
                    --sz2;
                } else {
                    if (l.merge(nums[j], -1, Integer::sum) == 0) {
                        l.remove(nums[j]);
                    }
                    s1 -= nums[j];
                    --sz1;
                }
            }
        }
        return ans;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public:
    long long minOperations(vector<int>& nums, int k) {
        multiset<int> l, r;
        long long s1 = 0, s2 = 0, ans = 1e18;
        for (int i = 0; i < nums.size(); ++i) {
            l.insert(nums[i]);
            s1 += nums[i];
            int y = *l.rbegin();
            l.erase(l.find(y));
            s1 -= y;
            r.insert(y);
            s2 += y;
            if (r.size() - l.size() > 1) {
                y = *r.begin();
                r.erase(r.find(y));
                s2 -= y;
                l.insert(y);
                s1 += y;
            }
            if (i >= k - 1) {
                long long x = *r.begin();
                ans = min(ans, s2 - x * (int) r.size() + x * (int) l.size() - s1);
                int j = i - k + 1;
                if (r.contains(nums[j])) {
                    r.erase(r.find(nums[j]));
                    s2 -= nums[j];
                } else {
                    l.erase(l.find(nums[j]));
                    s1 -= nums[j];
                }
            }
        }
        return ans;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
func minOperations(nums []int, k int) int64 {
    l := redblacktree.New[int, int]()
    r := redblacktree.New[int, int]()
    merge := func(st *redblacktree.Tree[int, int], x, v int) {
        c, _ := st.Get(x)
        if c+v == 0 {
            st.Remove(x)
        } else {
            st.Put(x, c+v)
        }
    }
    var s1, s2, sz1, sz2 int
    ans := math.MaxInt64
    for i, x := range nums {
        merge(l, x, 1)
        s1 += x
        y := l.Right().Key
        merge(l, y, -1)
        s1 -= y
        merge(r, y, 1)
        s2 += y
        sz2++
        if sz2-sz1 > 1 {
            y = r.Left().Key
            merge(r, y, -1)
            s2 -= y
            sz2--
            merge(l, y, 1)
            s1 += y
            sz1++
        }
        if j := i - k + 1; j >= 0 {
            ans = min(ans, s2-r.Left().Key*sz2+r.Left().Key*sz1-s1)
            if _, ok := r.Get(nums[j]); ok {
                merge(r, nums[j], -1)
                s2 -= nums[j]
                sz2--
            } else {
                merge(l, nums[j], -1)
                s1 -= nums[j]
                sz1--
            }
        }
    }
    return int64(ans)
}

评论