You are given an integer array nums of length n and an integer k.
You must select exactly k distinct non-empty subarrays nums[l..r] of nums. Subarrays may overlap, but the exact same subarray (same l and r) cannot be chosen more than once.
The value of a subarray nums[l..r] is defined as: max(nums[l..r]) – min(nums[l..r]).
The total value is the sum of the values of all chosen subarrays.
Return the maximum possible total value you can achieve.
你給予一個整數陣列 nums(長度為 n)和一個整數 k。 你需要選擇恰好 k 個不同的非空子陣列,子陣列可以重疊,但不能選擇完全相同(相同的 l 和 r)的子陣列超過一次。 每個子陣列的定義為:max(nums[l..r]) - min(nums[l..r]) total value 是所有選中子陣列的值總和。 回傳能夠得到的最大 total value。
Example 1:
Input: nums = [1,3,2], k = 2 Output: 4 Explanation: One optimal approach is: Choose nums[0..1] = [1, 3]. The maximum is 3 and the minimum is 1, giving a value of 3 - 1 = 2. Choose nums[0..2] = [1, 3, 2]. The maximum is still 3 and the minimum is still 1, so the value is also 3 - 1 = 2. Adding these gives 2 + 2 = 4.
Example 2:
Input: nums = [4,2,5,1], k = 3 Output: 12 Explanation: One optimal approach is: Choose nums[0..3] = [4, 2, 5, 1]. The maximum is 5 and the minimum is 1, giving a value of 5 - 1 = 4. Choose nums[1..3] = [2, 5, 1]. The maximum is 5 and the minimum is 1, so the value is also 4. Choose nums[2..3] = [5, 1]. The maximum is 5 and the minimum is 1, so the value is again 4. Adding these gives 4 + 4 + 4 = 12.
Solution:
題目要求選取 k 個不重疊的子陣列,並最大化 (max – min) 的總和。
為了能快速查詢任意子區間的最小值與最大值,我們使用 Segment Tree (BigO(log n))。
– 建立 Node,存放 [a,b] 區間以及 minVal、maxVal。
– 建立 Segment,用來存放子區間 [l,r] 及其差值。
– 使用 MaxHeap,每次取出目前差值最大的子區間。
– 當取出 [l,r] 後,將它「切割」成 [l,r-1] 與 [l+1,r],避免重疊與持續查找更多子區間。
– 用 Set 紀錄已使用的區間,避免重複推入。
– 重複取 k 次,累加答案並輸出。
Code 1: BigO(n + k log n)
class Node { l: Node | null = null; r: Node | null = null; a: number; b: number; maxVal: number = 0; minVal: number = 0; constructor(a: number, b: number) { this.a = a; this.b = b; } } class Segment { l: number; r: number; val: number; constructor(l: number, r: number, val: number) { this.l = l; this.r = r; this.val = val; } } class CustomizeMaxHeap { data: Segment[] = []; size() { return this.data.length; } push(seg: Segment) { this.data.push(seg); this._siftUp(this.data.length - 1); } pop(): Segment | undefined { if (this.data.length === 0) return undefined; const top = this.data[0]; const last = this.data.pop()!; if (this.data.length > 0) { this.data[0] = last; this._siftDown(0); } return top; } private _siftUp(i: number) { while (i > 0) { const p = Math.floor((i - 1) / 2); if (this.data[i].val > this.data[p].val) { [this.data[i], this.data[p]] = [this.data[p], this.data[i]]; i = p; } else break; } } private _siftDown(i: number) { const n = this.data.length; while (true) { let largest = i; const l = i * 2 + 1, r = i * 2 + 2; if (l < n && this.data[l].val > this.data[largest].val) largest = l; if (r < n && this.data[r].val > this.data[largest].val) largest = r; if (largest !== i) { [this.data[i], this.data[largest]] = [this.data[largest], this.data[i]]; i = largest; } else break; } } } function buildSegmentTree(nums: number[], a: number, b: number): Node { const node = new Node(a, b); if (a === b) { node.minVal = node.maxVal = nums[a]; return node; } const mid = Math.floor((a + b) / 2); node.l = buildSegmentTree(nums, a, mid); node.r = buildSegmentTree(nums, mid + 1, b); node.minVal = Math.min(node.l.minVal, node.r.minVal); node.maxVal = Math.max(node.l.maxVal, node.r.maxVal); return node; } function querySegmentTree(node: Node, l: number, r: number): [number, number] { if (node.a >= l && node.b <= r) return [node.minVal, node.maxVal]; if (node.b < l || node.a > r) return [1e9, -1e9]; const [minL, maxL] = querySegmentTree(node.l!, l, r); const [minR, maxR] = querySegmentTree(node.r!, l, r); return [Math.min(minL, minR), Math.max(maxL, maxR)]; } function makeKey(l: number, r: number) { return `${l},${r}`; } function maxTotalValue(nums: number[], k: number): number { const n = nums.length; const root = buildSegmentTree(nums, 0, n - 1); const used = new Set(); const heap = new CustomizeMaxHeap(); const [minWhole, maxWhole] = querySegmentTree(root, 0, n - 1); heap.push(new Segment(0, n - 1, maxWhole - minWhole)); used.add(makeKey(0, n - 1)); let ans = 0; while (k-- > 0 && heap.size() > 0) { const segment = heap.pop()!; ans += segment.val; const { l, r } = segment; if (l < r) { const leftKey = makeKey(l, r - 1); if (!used.has(leftKey)) { const [minL, maxL] = querySegmentTree(root, l, r - 1); heap.push(new Segment(l, r - 1, maxL - minL)); used.add(leftKey); } const rightKey = makeKey(l + 1, r); if (!used.has(rightKey)) { const [minR, maxR] = querySegmentTree(root, l + 1, r); heap.push(new Segment(l + 1, r, maxR - minR)); used.add(rightKey); } } } return ans; }
FlowChart:
Example 1
heap CustomizeMaxHeap { data: [ Segment { l: 0, r: 2, val: 2 } ] } used Set(1) { '0,2' } Choose nums[0..2] = [1,3,2], value=2, ans=2 segment Segment { l: 0, r: 2, val: 2 } ans 2 Choose nums[0..1] = [1,3], value=2, ans=4 segment Segment { l: 0, r: 1, val: 2 } ans 4