Welcome to Subscribe On Youtube
3505. Minimum Operations to Make Elements Within K Subarrays Equal
Description
You are given an integer array nums and two integers, x and k. You can perform the following operation any number of times (including zero):
- Increase or decrease any element of
numsby 1.
Return the minimum number of operations needed to have at least k non-overlapping subarrays of size exactly x in nums, where all elements within each subarray are equal.
Example 1:
Input: nums = [5,-2,1,3,7,3,6,4,-1], x = 3, k = 2
Output: 8
Explanation:
- Use 3 operations to add 3 to
nums[1]and use 2 operations to subtract 2 fromnums[3]. The resulting array is[5, 1, 1, 1, 7, 3, 6, 4, -1]. - Use 1 operation to add 1 to
nums[5]and use 2 operations to subtract 2 fromnums[6]. The resulting array is[5, 1, 1, 1, 7, 4, 4, 4, -1]. - Now, all elements within each subarray
[1, 1, 1](from indices 1 to 3) and[4, 4, 4](from indices 5 to 7) are equal. Since 8 total operations were used, 8 is the output.
Example 2:
Input: nums = [9,-2,-2,-2,1,5], x = 2, k = 2
Output: 3
Explanation:
- Use 3 operations to subtract 3 from
nums[4]. The resulting array is[9, -2, -2, -2, -2, 5]. - Now, all elements within each subarray
[-2, -2](from indices 1 to 2) and[-2, -2](from indices 3 to 4) are equal. Since 3 operations were used, 3 is the output.
Constraints:
2 <= nums.length <= 105-106 <= nums[i] <= 1062 <= x <= nums.length1 <= k <= 152 <= k * x <= nums.length
Solutions
Solution 1
-
class MedianCost { TreeMap<Integer, Integer> l = new TreeMap<>(); TreeMap<Integer, Integer> r = new TreeMap<>(); int n1 = 0, n2 = 0; long s1 = 0, s2 = 0; void addTo(TreeMap<Integer, Integer> tree, int x) { tree.merge(x, 1, Integer::sum); } void removeFrom(TreeMap<Integer, Integer> tree, int x) { int cnt = tree.get(x); if (cnt == 1) { tree.remove(x); } else { tree.put(x, cnt - 1); } } void adjust() { while (n1 - n2 > 1) { int x = l.lastKey(); removeFrom(l, x); n1--; s1 -= x; addTo(r, x); n2++; s2 += x; } while (n2 > n1) { int x = r.firstKey(); removeFrom(r, x); n2--; s2 -= x; addTo(l, x); n1++; s1 += x; } } void add(int x) { addTo(r, x); n2++; s2 += x; int y = r.firstKey(); removeFrom(r, y); n2--; s2 -= y; addTo(l, y); n1++; s1 += y; adjust(); } void remove(int x) { if (l.containsKey(x)) { removeFrom(l, x); n1--; s1 -= x; } else { removeFrom(r, x); n2--; s2 -= x; } adjust(); } long query() { long mid = l.lastKey(); return n1 * mid - s1 + s2 - n2 * mid; } } class Solution { public long minOperations(int[] nums, int x, int k) { int n = nums.length; long INF = Long.MAX_VALUE / 4; long[][] f = new long[n + 1][k + 1]; for (long[] row : f) { Arrays.fill(row, INF); } f[0][0] = 0; MedianCost mc = new MedianCost(); for (int i = 1; i <= n; i++) { mc.add(nums[i - 1]); long w = mc.query(); for (int j = 0; j <= k; j++) { f[i][j] = f[i - 1][j]; if (j > 0 && i >= x) { f[i][j] = Math.min(f[i][j], f[i - x][j - 1] + w); } } if (i >= x) { mc.remove(nums[i - x]); } } return f[n][k]; } } -
class MedianCost { multiset<int> l, r; long long s1 = 0, s2 = 0; public: void adjust() { while ((long long) l.size() - (long long) r.size() > 1) { int v = *l.rbegin(); l.erase(prev(l.end())); s1 -= v; r.insert(v); s2 += v; } while (r.size() > l.size()) { int v = *r.begin(); r.erase(r.begin()); s2 -= v; l.insert(v); s1 += v; } } void add(int x) { r.insert(x); s2 += x; int y = *r.begin(); r.erase(r.begin()); s2 -= y; l.insert(y); s1 += y; adjust(); } void remove(int x) { auto it = l.find(x); if (it != l.end()) { l.erase(it); s1 -= x; } else { r.erase(r.find(x)); s2 -= x; } adjust(); } long long query() { long long mid = *l.rbegin(); return (long long) l.size() * mid - s1 + s2 - (long long) r.size() * mid; } }; class Solution { public: long long minOperations(vector<int>& nums, int x, int k) { int n = nums.size(); const long long INF = LLONG_MAX / 4; vector<vector<long long>> f(n + 1, vector<long long>(k + 1, INF)); f[0][0] = 0; MedianCost mc; for (int i = 1; i <= n; ++i) { mc.add(nums[i - 1]); long long w = mc.query(); for (int j = 0; j <= k; ++j) { f[i][j] = f[i - 1][j]; if (j > 0 && i >= x) { f[i][j] = min(f[i][j], f[i - x][j - 1] + w); } } if (i >= x) { mc.remove(nums[i - x]); } } return f[n][k]; } }; -
class MedianCost: def __init__(self): self.l = SortedList() self.r = SortedList() self.s1 = 0 self.s2 = 0 def add(self, x: int): self.r.add(x) self.s2 += x y = self.r.pop(0) self.s2 -= y self.l.add(y) self.s1 += y self.adjust() def adjust(self): while len(self.l) - len(self.r) > 1: x = self.l.pop() self.s1 -= x self.r.add(x) self.s2 += x while len(self.r) > len(self.l): x = self.r.pop(0) self.s2 -= x self.l.add(x) self.s1 += x def remove(self, x: int): if x in self.l: self.l.remove(x) self.s1 -= x else: self.r.remove(x) self.s2 -= x self.adjust() def query(self) -> int: mid = self.l[-1] return len(self.l) * mid - self.s1 + self.s2 - len(self.r) * mid class Solution: def minOperations(self, nums: List[int], x: int, k: int) -> int: n = len(nums) f = [[inf] * (k + 1) for _ in range(n + 1)] f[0][0] = 0 mc = MedianCost() for i, v in enumerate(nums, 1): mc.add(v) w = mc.query() for j in range(k + 1): f[i][j] = min(f[i][j], f[i - 1][j]) if j > 0 and i >= x: f[i][j] = min(f[i][j], f[i - x][j - 1] + w) if i >= x: mc.remove(nums[i - x]) return f[n][k] -
type MedianCost struct { l, r *redblacktree.Tree n1, n2 int s1, s2 int64 } func NewMedianCost() *MedianCost { return &MedianCost{ l: redblacktree.NewWithIntComparator(), r: redblacktree.NewWithIntComparator(), } } func addTo(tree *redblacktree.Tree, x int) { v, ok := tree.Get(x) if ok { tree.Put(x, v.(int)+1) } else { tree.Put(x, 1) } } func removeFrom(tree *redblacktree.Tree, x int) { v, _ := tree.Get(x) if v.(int) == 1 { tree.Remove(x) } else { tree.Put(x, v.(int)-1) } } func (mc *MedianCost) adjust() { for mc.n1-mc.n2 > 1 { x := mc.l.Right().Key.(int) removeFrom(mc.l, x) mc.n1-- mc.s1 -= int64(x) addTo(mc.r, x) mc.n2++ mc.s2 += int64(x) } for mc.n2 > mc.n1 { x := mc.r.Left().Key.(int) removeFrom(mc.r, x) mc.n2-- mc.s2 -= int64(x) addTo(mc.l, x) mc.n1++ mc.s1 += int64(x) } } func (mc *MedianCost) add(x int) { addTo(mc.r, x) mc.n2++ mc.s2 += int64(x) y := mc.r.Left().Key.(int) removeFrom(mc.r, y) mc.n2-- mc.s2 -= int64(y) addTo(mc.l, y) mc.n1++ mc.s1 += int64(y) mc.adjust() } func (mc *MedianCost) remove(x int) { if _, ok := mc.l.Get(x); ok { removeFrom(mc.l, x) mc.n1-- mc.s1 -= int64(x) } else { removeFrom(mc.r, x) mc.n2-- mc.s2 -= int64(x) } mc.adjust() } func (mc *MedianCost) query() int64 { mid := int64(mc.l.Right().Key.(int)) return int64(mc.n1)*mid - mc.s1 + mc.s2 - int64(mc.n2)*mid } func minOperations(nums []int, x int, k int) int64 { n := len(nums) const INF int64 = math.MaxInt64 / 4 f := make([][]int64, n+1) for i := range f { f[i] = make([]int64, k+1) for j := range f[i] { f[i][j] = INF } } f[0][0] = 0 mc := NewMedianCost() for i := 1; i <= n; i++ { mc.add(nums[i-1]) w := mc.query() for j := 0; j <= k; j++ { f[i][j] = f[i-1][j] if j > 0 && i >= x { f[i][j] = min(f[i][j], f[i-x][j-1]+w) } } if i >= x { mc.remove(nums[i-x]) } } return f[n][k] } -
class Node { key: number; pri: number; cnt: number; left: Node | null = null; right: Node | null = null; constructor(key: number) { this.key = key; this.pri = Math.random(); this.cnt = 1; } } class Treap { root: Node | null = null; rotateRight(o: Node): Node { const p = o.left!; o.left = p.right; p.right = o; return p; } rotateLeft(o: Node): Node { const p = o.right!; o.right = p.left; p.left = o; return p; } merge(a: Node | null, b: Node | null): Node | null { if (!a) return b; if (!b) return a; if (a.pri > b.pri) { a.right = this.merge(a.right, b); return a; } b.left = this.merge(a, b.left); return b; } insert(o: Node | null, x: number): Node { if (!o) return new Node(x); if (x === o.key) { o.cnt++; } else if (x < o.key) { o.left = this.insert(o.left, x); if (o.left.pri > o.pri) { o = this.rotateRight(o); } } else { o.right = this.insert(o.right, x); if (o.right.pri > o.pri) { o = this.rotateLeft(o); } } return o; } erase(o: Node | null, x: number): Node | null { if (!o) return null; if (x < o.key) { o.left = this.erase(o.left, x); } else if (x > o.key) { o.right = this.erase(o.right, x); } else { if (o.cnt > 1) { o.cnt--; } else { return this.merge(o.left, o.right); } } return o; } add(x: number): void { this.root = this.insert(this.root, x); } remove(x: number): void { this.root = this.erase(this.root, x); } contains(x: number): boolean { let o = this.root; while (o) { if (x === o.key) return true; o = x < o.key ? o.left : o.right; } return false; } first(): number { let o = this.root!; while (o.left) o = o.left; return o.key; } last(): number { let o = this.root!; while (o.right) o = o.right; return o.key; } } class MedianCost { l = new Treap(); r = new Treap(); n1 = 0; n2 = 0; s1 = 0; s2 = 0; adjust(): void { while (this.n1 - this.n2 > 1) { const x = this.l.last(); this.l.remove(x); this.n1--; this.s1 -= x; this.r.add(x); this.n2++; this.s2 += x; } while (this.n2 > this.n1) { const x = this.r.first(); this.r.remove(x); this.n2--; this.s2 -= x; this.l.add(x); this.n1++; this.s1 += x; } } add(x: number): void { this.r.add(x); this.n2++; this.s2 += x; const y = this.r.first(); this.r.remove(y); this.n2--; this.s2 -= y; this.l.add(y); this.n1++; this.s1 += y; this.adjust(); } remove(x: number): void { if (this.l.contains(x)) { this.l.remove(x); this.n1--; this.s1 -= x; } else { this.r.remove(x); this.n2--; this.s2 -= x; } this.adjust(); } query(): number { const mid = this.l.last(); return this.n1 * mid - this.s1 + this.s2 - this.n2 * mid; } } function minOperations(nums: number[], x: number, k: number): number { const n = nums.length; const INF = Number.MAX_SAFE_INTEGER; const f: number[][] = Array.from({ length: n + 1 }, () => new Array(k + 1).fill(INF)); f[0][0] = 0; const mc = new MedianCost(); for (let i = 1; i <= n; i++) { mc.add(nums[i - 1]); const w = mc.query(); for (let j = 0; j <= k; j++) { f[i][j] = f[i - 1][j]; if (j > 0 && i >= x) { f[i][j] = Math.min(f[i][j], f[i - x][j - 1] + w); } } if (i >= x) { mc.remove(nums[i - x]); } } return f[n][k]; }