3430. 最多 K 个元素的子数组的最值之和
给你一个整数数组 nums
和一个 正 整数 k
。 返回 最多 有 k
个元素的所有子数组的 最大 和 最小 元素之和。
示例 1:
输入:nums = [1,2,3], k = 2
输出:20
解释:
最多 2 个元素的 nums
的子数组:
子数组 | 最小 | 最大 | 和 |
---|---|---|---|
[1] |
1 | 1 | 2 |
[2] |
2 | 2 | 4 |
[3] |
3 | 3 | 6 |
[1, 2] |
1 | 2 | 3 |
[2, 3] |
2 | 3 | 5 |
总和 | 20 |
输出为 20 。
示例 2:
输入:nums = [1,-3,1], k = 2
输出:-6
解释:
最多 2 个元素的 nums
的子数组:
子数组 | 最小 | 最大 | 和 |
---|---|---|---|
[1] |
1 | 1 | 2 |
[-3] |
-3 | -3 | -6 |
[1] |
1 | 1 | 2 |
[1, -3] |
-3 | 1 | -2 |
[-3, 1] |
-3 | 1 | -2 |
总和 | -6 |
输出为 -6 。
提示:
1 <= nums.length <= 80000
1 <= k <= nums.length
-106 <= nums[i] <= 106
相似题目
原站题解
python3 解法, 执行用时: 1248 ms, 内存消耗: 30.1 MB, 提交时间: 2025-01-27 09:04:26
class Solution: def sumSubarrayMins(self, nums: List[int], k: int) -> int: count = lambda m: (m * 2 - k + 1) * k // 2 if m > k else (m + 1) * m // 2 ans = 0 st = [-1] for r, x in enumerate(nums + [-inf]): while len(st) > 1 and nums[st[-1]] >= x: i = st.pop() l = st[-1] cnt = count(r - l - 1) - count(i - l - 1) - count(r - i - 1) ans += nums[i] * cnt st.append(r) return ans def minMaxSubarraySum(self, nums: List[int], k: int) -> int: ans = self.sumSubarrayMins(nums, k) ans -= self.sumSubarrayMins([-x for x in nums], k) return ans
python3 解法, 执行用时: 730 ms, 内存消耗: 30.6 MB, 提交时间: 2025-01-27 09:04:11
class Solution: # 计算最小值的贡献 def sumSubarrayMins(self, nums: List[int], k: int) -> int: ans = 0 st = [-1] for r, x in enumerate(nums + [-inf]): r0 = r while len(st) > 1 and nums[st[-1]] >= x: i = st.pop() l = st[-1] if r - l - 1 <= k: cnt = (i - l) * (r - i) ans += nums[i] * cnt # 累加贡献 else: l = max(l, i - k) r = min(r, i + k) # 左端点 > r-k 的子数组个数 cnt = (r - i) * (i - (r - k)) # 左端点 <= r-k 的子数组个数 cnt2 = (l + r + k - i * 2 + 1) * (r - l - k) // 2 ans += nums[i] * (cnt + cnt2) # 累加贡献 st.append(r0) return ans def minMaxSubarraySum(self, nums: List[int], k: int) -> int: ans = self.sumSubarrayMins(nums, k) # 所有元素取反,就可以复用同一份代码求最大值的贡献了 ans -= self.sumSubarrayMins([-x for x in nums], k) return ans
python3 解法, 执行用时: 807 ms, 内存消耗: 30.7 MB, 提交时间: 2025-01-27 09:03:59
class Solution: # 计算最小值的贡献 def sumSubarrayMins(self, nums: List[int], k: int) -> int: n = len(nums) # 左边界 left[i] 为左侧严格小于 nums[i] 的最近元素位置(不存在时为 -1) left = [-1] * n # 右边界 right[i] 为右侧小于等于 nums[i] 的最近元素位置(不存在时为 n) right = [n] * n st = [] for i, x in enumerate(nums): while st and x <= nums[st[-1]]: right[st.pop()] = i # i 是栈顶的右边界 if st: left[i] = st[-1] st.append(i) ans = 0 for i, (x, l, r) in enumerate(zip(nums, left, right)): if r - l - 1 <= k: cnt = (i - l) * (r - i) ans += x * cnt # 累加贡献 else: l = max(l, i - k) r = min(r, i + k) # 左端点 > r-k 的子数组个数 cnt = (r - i) * (i - (r - k)) # 左端点 <= r-k 的子数组个数 cnt2 = (l + r + k - i * 2 + 1) * (r - l - k) // 2 ans += x * (cnt + cnt2) # 累加贡献 return ans def minMaxSubarraySum(self, nums: List[int], k: int) -> int: ans = self.sumSubarrayMins(nums, k) # 所有元素取反,就可以复用同一份代码求最大值的贡献了 ans -= self.sumSubarrayMins([-x for x in nums], k) return ans
golang 解法, 执行用时: 40 ms, 内存消耗: 10.2 MB, 提交时间: 2025-01-27 09:03:36
func minMaxSubarraySum(nums []int, k int) int64 { count := func(m int) int { if m <= k { return (m + 1) * m / 2 } return (m*2 - k + 1) * k / 2 } // 计算最小值的贡献 sumSubarrayMins := func() (res int) { st := []int{-1} // 哨兵 for r, x := range nums { for len(st) > 1 && nums[st[len(st)-1]] >= x { i := st[len(st)-1] st = st[:len(st)-1] l := st[len(st)-1] cnt := count(r-l-1) - count(i-l-1) - count(r-i-1) res += nums[i] * cnt // 累加贡献 } st = append(st, r) } return } nums = append(nums, math.MinInt) ans := sumSubarrayMins() // 所有元素取反(求最大值),就可以复用同一份代码了 for i := range nums { nums[i] = -nums[i] } ans -= sumSubarrayMins() return int64(ans) }
golang 解法, 执行用时: 18 ms, 内存消耗: 10 MB, 提交时间: 2025-01-27 09:03:26
func minMaxSubarraySum(nums []int, k int) int64 { // 计算最小值的贡献 sumSubarrayMins := func() (res int) { st := []int{-1} // 哨兵 for r, x := range nums { r0 := r for len(st) > 1 && nums[st[len(st)-1]] >= x { i := st[len(st)-1] st = st[:len(st)-1] l := st[len(st)-1] if r-l-1 <= k { cnt := (i - l) * (r - i) res += nums[i] * cnt // 累加贡献 } else { l = max(l, i-k) r = min(r, i+k) // 左端点 > r-k 的子数组个数 cnt := (r - i) * (i - (r - k)) // 左端点 <= r-k 的子数组个数 cnt2 := (l + r + k - i*2 + 1) * (r - l - k) / 2 res += nums[i] * (cnt + cnt2) // 累加贡献 } } st = append(st, r0) } return } nums = append(nums, math.MinInt) ans := sumSubarrayMins() // 所有元素取反(求最大值),就可以复用同一份代码了 for i := range nums { nums[i] = -nums[i] } ans -= sumSubarrayMins() return int64(ans) }
golang 解法, 执行用时: 26 ms, 内存消耗: 10.8 MB, 提交时间: 2025-01-27 09:03:16
func minMaxSubarraySum(nums []int, k int) int64 { // 计算最小值的贡献 sumSubarrayMins := func() (res int) { n := len(nums) // 左边界 left[i] 为左侧严格小于 nums[i] 的最近元素位置(不存在时为 -1) left := make([]int, n) // 右边界 right[i] 为右侧小于等于 nums[i] 的最近元素位置(不存在时为 n) right := make([]int, n) st := []int{-1} // 哨兵,方便赋值 left for i, x := range nums { for len(st) > 1 && x <= nums[st[len(st)-1]] { right[st[len(st)-1]] = i // i 是栈顶的右边界 st = st[:len(st)-1] } left[i] = st[len(st)-1] st = append(st, i) } for _, i := range st[1:] { right[i] = n } for i, x := range nums { l, r := left[i], right[i] if r-l-1 <= k { cnt := (i - l) * (r - i) res += x * cnt // 累加贡献 } else { l = max(l, i-k) r = min(r, i+k) // 左端点 > r-k 的子数组个数 cnt := (r - i) * (i - (r - k)) // 左端点 <= r-k 的子数组个数 cnt2 := (l + r + k - i*2 + 1) * (r - l - k) / 2 res += x * (cnt + cnt2) // 累加贡献 } } return } ans := sumSubarrayMins() // 所有元素取反,就可以复用同一份代码求最大值的贡献了 for i := range nums { nums[i] = -nums[i] } ans -= sumSubarrayMins() return int64(ans) }
cpp 解法, 执行用时: 122 ms, 内存消耗: 146.9 MB, 提交时间: 2025-01-27 09:02:39
class Solution { public: long long minMaxSubarraySum(vector<int>& nums, int k) { auto count = [&](int m) -> long long { return m > k ? 1LL * (m * 2 - k + 1) * k / 2 : 1LL * (m + 1) * m / 2; }; // 计算最小值的贡献 auto sumSubarrayMins = [&]() -> long long { long long res = 0; stack<int> st; st.push(-1); // 哨兵 for (int r = 0; r < nums.size(); r++) { while (st.size() > 1 && nums[st.top()] >= nums[r]) { int i = st.top(); st.pop(); int l = st.top(); long long cnt = count(r - l - 1) - count(i - l - 1) - count(r - i - 1); res += nums[i] * cnt; // 累加贡献 } st.push(r); } return res; }; nums.push_back(INT_MIN / 2); long long ans = sumSubarrayMins(); // 所有元素取反,就可以复用同一份代码求最大值的贡献了 for (int& x : nums) { x = -x; } nums.back() *= -1; ans -= sumSubarrayMins(); return ans; } };
cpp 解法, 执行用时: 78 ms, 内存消耗: 147 MB, 提交时间: 2025-01-27 09:02:28
class Solution { public: long long minMaxSubarraySum(vector<int>& nums, int k) { // 计算最小值的贡献 auto sumSubarrayMins = [&]() -> long long { long long res = 0; stack<int> st; st.push(-1); // 哨兵 for (int r = 0; r < nums.size(); r++) { while (st.size() > 1 && nums[st.top()] >= nums[r]) { int i = st.top(); st.pop(); int l = st.top(); int x = nums[i]; if (r - l - 1 <= k) { long long cnt = 1LL * (i - l) * (r - i); res += x * cnt; // 累加贡献 } else { l = max(l, i - k); int R = min(r, i + k); // 左端点 > r-k 的子数组个数 long long cnt = 1LL * (R - i) * (i - (R - k)); // 左端点 <= r-k 的子数组个数 long long cnt2 = 1LL * (l + R + k - i * 2 + 1) * (R - l - k) / 2; res += x * (cnt + cnt2); // 累加贡献 } } st.push(r); } return res; }; nums.push_back(INT_MIN / 2); long long ans = sumSubarrayMins(); // 所有元素取反,就可以复用同一份代码求最大值的贡献了 for (int& x : nums) { x = -x; } nums.back() *= -1; ans -= sumSubarrayMins(); return ans; } };
cpp 解法, 执行用时: 126 ms, 内存消耗: 170.9 MB, 提交时间: 2025-01-27 09:02:19
class Solution { public: long long minMaxSubarraySum(vector<int>& nums, int k) { // 计算最小值的贡献 auto sumSubarrayMins = [&]() -> long long { int n = nums.size(); // 左边界 left[i] 为左侧严格小于 nums[i] 的最近元素位置(不存在时为 -1) vector<int> left(n, -1); // 右边界 right[i] 为右侧小于等于 nums[i] 的最近元素位置(不存在时为 n) vector<int> right(n, n); stack<int> st; st.push(-1); // 哨兵,方便赋值 left for (int i = 0; i < n; i++) { int x = nums[i]; while (st.size() > 1 && x <= nums[st.top()]) { right[st.top()] = i; // i 是栈顶的右边界 st.pop(); } left[i] = st.top(); st.push(i); } long long res = 0; for (int i = 0; i < n; i++) { int x = nums[i], l = left[i], r = right[i]; if (r - l - 1 <= k) { long long cnt = 1LL * (i - l) * (r - i); res += x * cnt; // 累加贡献 } else { l = max(l, i - k); r = min(r, i + k); // 左端点 > r-k 的子数组个数 long long cnt = 1LL * (r - i) * (i - (r - k)); // 左端点 <= r-k 的子数组个数 long long cnt2 = 1LL * (l + r + k - i * 2 + 1) * (r - l - k) / 2; res += x * (cnt + cnt2); // 累加贡献 } } return res; }; long long ans = sumSubarrayMins(); // 所有元素取反,就可以复用同一份代码求最大值的贡献了 for (int& x : nums) { x = -x; } ans -= sumSubarrayMins(); return ans; } };
java 解法, 执行用时: 87 ms, 内存消耗: 56 MB, 提交时间: 2025-01-27 09:02:03
class Solution { public long minMaxSubarraySum(int[] nums, int k) { long ans = sumSubarrayMins(nums, k); // 所有元素取反,就可以复用同一份代码求最大值的贡献了 for (int i = 0; i < nums.length; i++) { nums[i] = -nums[i]; } ans -= sumSubarrayMins(nums, k); return ans; } // 计算最小值的贡献 private long sumSubarrayMins(int[] nums, int k) { int n = nums.length; Deque<Integer> st = new ArrayDeque<>(); st.push(-1); long res = 0; for (int r = 0; r <= n; r++) { int v = r < n ? nums[r] : Integer.MIN_VALUE; // 假设 nums 末尾有个 Integer.MIN_VALUE while (st.size() > 1 && nums[st.peek()] >= v) { int i = st.pop(); int l = st.peek(); long cnt = count(r - l - 1, k) - count(i - l - 1, k) - count(r - i - 1, k); res += nums[i] * cnt; // 累加贡献 } st.push(r); } return res; } private long count(int m, int k) { return m > k ? ((long) m * 2 - k + 1) * k / 2 : ((long) m + 1) * m / 2; } }
java 解法, 执行用时: 63 ms, 内存消耗: 55.7 MB, 提交时间: 2025-01-27 09:01:53
class Solution { public long minMaxSubarraySum(int[] nums, int k) { long ans = sumSubarrayMins(nums, k); // 所有元素取反,就可以复用同一份代码求最大值的贡献了 for (int i = 0; i < nums.length; i++) { nums[i] = -nums[i]; } ans -= sumSubarrayMins(nums, k); return ans; } // 计算最小值的贡献 private long sumSubarrayMins(int[] nums, int k) { int n = nums.length; Deque<Integer> st = new ArrayDeque<>(); st.push(-1); long res = 0; for (int r = 0; r <= n; r++) { int v = r < n ? nums[r] : Integer.MIN_VALUE; // 假设 nums 末尾有个 Integer.MIN_VALUE while (st.size() > 1 && nums[st.peek()] >= v) { int i = st.pop(); int l = st.peek(); int x = nums[i]; if (r - l - 1 <= k) { long cnt = (long) (i - l) * (r - i); res += x * cnt; // 累加贡献 } else { l = Math.max(l, i - k); int R = Math.min(r, i + k); // 左端点 > R-k 的子数组个数 long cnt = (long) (R - i) * (i - (R - k)); // 左端点 <= r-k 的子数组个数 long cnt2 = (long) (l + R + k - i * 2 + 1) * (R - l - k) / 2; res += x * (cnt + cnt2); // 累加贡献 } } st.push(r); } return res; } }
java 解法, 执行用时: 64 ms, 内存消耗: 56.7 MB, 提交时间: 2025-01-27 09:01:41
class Solution { public long minMaxSubarraySum(int[] nums, int k) { long ans = sumSubarrayMins(nums, k); // 所有元素取反,就可以复用同一份代码求最大值的贡献了 for (int i = 0; i < nums.length; i++) { nums[i] = -nums[i]; } ans -= sumSubarrayMins(nums, k); return ans; } // 计算最小值的贡献 private long sumSubarrayMins(int[] nums, int k) { int n = nums.length; // 左边界 left[i] 为左侧严格小于 nums[i] 的最近元素位置(不存在时为 -1) int[] left = new int[n]; Arrays.fill(left, -1); // 右边界 right[i] 为右侧小于等于 nums[i] 的最近元素位置(不存在时为 n) int[] right = new int[n]; Arrays.fill(right, n); Deque<Integer> st = new ArrayDeque<>(); st.push(-1); // 哨兵,方便赋值 left for (int i = 0; i < n; i++) { while (st.size() > 1 && nums[i] <= nums[st.peek()]) { right[st.pop()] = i; // i 是栈顶的右边界 } left[i] = st.peek(); st.push(i); } long res = 0; for (int i = 0; i < n; i++) { int x = nums[i]; int l = left[i]; int r = right[i]; if (r - l - 1 <= k) { long cnt = (long) (i - l) * (r - i); res += x * cnt; // 累加贡献 } else { l = Math.max(l, i - k); r = Math.min(r, i + k); // 左端点 > r-k 的子数组个数 long cnt = (long) (r - i) * (i - (r - k)); // 左端点 <= r-k 的子数组个数 long cnt2 = (long) (l + r + k - i * 2 + 1) * (r - l - k) / 2; res += x * (cnt + cnt2); // 累加贡献 } } return res; } }