当前位置: 首页 > news >正文

DeepSeek LeetCode 3430. 最多 K 个元素的子数组的最值之和 Java实现

LeetCode 3430. 最多 K 个元素的子数组的最值之和

题目分析

给定一个整数数组 nums 和一个整数 k,需要找出所有长度不超过 k 的连续子数组中,最大值和最小值之和的总和。

解题思路

核心思想

对于每个元素,计算它作为最大值和最小值时,对答案的贡献次数。

关键步骤

1. 计算作为最大值的贡献
· 找到每个元素左边第一个大于它的元素位置
· 找到每个元素右边第一个大于等于它的元素位置(处理重复元素)
· 计算以该元素为最大值的子数组数量,且长度 ≤ k
2. 计算作为最小值的贡献
· 找到每个元素左边第一个小于它的元素位置
· 找到每个元素右边第一个小于等于它的元素位置(处理重复元素)
· 计算以该元素为最小值的子数组数量,且长度 ≤ k
3. 答案 = 最大值贡献总和 + 最小值贡献总和

时间复杂度

· O(n),使用单调栈

Java实现

```java
class Solution {
public long minMaxSubarraySum(int[] nums, int k) {
int n = nums.length;
long ans = 0;

// 计算作为最大值的贡献
int[] leftGreater = new int[n];
int[] rightGreaterEqual = new int[n];
Deque<Integer> stack = new ArrayDeque<>();

// 左边第一个大于当前元素的索引
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && nums[stack.peek()] <= nums[i]) {
stack.pop();
}
leftGreater[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}

stack.clear();
// 右边第一个大于等于当前元素的索引
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && nums[stack.peek()] < nums[i]) {
stack.pop();
}
rightGreaterEqual[i] = stack.isEmpty() ? n : stack.peek();
stack.push(i);
}

// 计算最大值的贡献
for (int i = 0; i < n; i++) {
int left = i - leftGreater[i];
int right = rightGreaterEqual[i] - i;
ans += (long) nums[i] * countSubarrays(left, right, k);
}

// 计算作为最小值的贡献
int[] leftSmaller = new int[n];
int[] rightSmallerEqual = new int[n];
stack.clear();

// 左边第一个小于当前元素的索引
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && nums[stack.peek()] >= nums[i]) {
stack.pop();
}
leftSmaller[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}

stack.clear();
// 右边第一个小于等于当前元素的索引
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && nums[stack.peek()] > nums[i]) {
stack.pop();
}
rightSmallerEqual[i] = stack.isEmpty() ? n : stack.peek();
stack.push(i);
}

// 计算最小值的贡献
for (int i = 0; i < n; i++) {
int left = i - leftSmaller[i];
int right = rightSmallerEqual[i] - i;
ans += (long) nums[i] * countSubarrays(left, right, k);
}

return ans;
}

// 计算在 left × right 的网格中,包含当前元素且长度 ≤ k 的子数组数量
private long countSubarrays(int left, int right, int k) {
long total = 0;
// 枚举左边取 x 个,右边取 y 个,满足 x + y + 1 ≤ k
// x ∈ [0, left-1], y ∈ [0, right-1]
int maxX = Math.min(left - 1, k - 1);
int maxY = Math.min(right - 1, k - 1);

// 使用数学公式计算
for (int x = 0; x <= maxX; x++) {
int remain = k - 1 - x;
int yLimit = Math.min(maxY, remain);
if (yLimit >= 0) {
total += (yLimit + 1);
}
}
return total;
}
}
```

优化版本(使用前缀和优化 countSubarrays)AC

```java
class Solution {
public long minMaxSubarraySum(int[] nums, int k) {
int n = nums.length;
long ans = 0;

// 计算最大值的贡献
int[] leftGreater = new int[n];
int[] rightGreaterEqual = new int[n];
Deque<Integer> stack = new ArrayDeque<>();

for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && nums[stack.peek()] <= nums[i]) {
stack.pop();
}
leftGreater[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}

stack.clear();
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && nums[stack.peek()] < nums[i]) {
stack.pop();
}
rightGreaterEqual[i] = stack.isEmpty() ? n : stack.peek();
stack.push(i);
}

for (int i = 0; i < n; i++) {
int left = i - leftGreater[i];
int right = rightGreaterEqual[i] - i;
ans += (long) nums[i] * countSubarraysOptimized(left, right, k);
}

// 计算最小值的贡献
int[] leftSmaller = new int[n];
int[] rightSmallerEqual = new int[n];
stack.clear();

for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && nums[stack.peek()] >= nums[i]) {
stack.pop();
}
leftSmaller[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}

stack.clear();
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && nums[stack.peek()] > nums[i]) {
stack.pop();
}
rightSmallerEqual[i] = stack.isEmpty() ? n : stack.peek();
stack.push(i);
}

for (int i = 0; i < n; i++) {
int left = i - leftSmaller[i];
int right = rightSmallerEqual[i] - i;
ans += (long) nums[i] * countSubarraysOptimized(left, right, k);
}

return ans;
}

// 优化版本:使用数学公式 O(1) 计算
private long countSubarraysOptimized(int left, int right, int k) {
long l = Math.min(left, k);
long r = Math.min(right, k);

if (l == 0 || r == 0) return 0;

// 计算所有可能的 (x, y) 对数,其中 x ∈ [0, l-1], y ∈ [0, r-1], x + y ≤ k-1
long total = 0;
long maxSum = k - 1;

if (l + r - 2 <= maxSum) {
// 所有组合都满足
return l * r;
}

// 分情况计算
// 情况1: x 较小,y 可以取全部
for (long x = 0; x < l && x <= maxSum; x++) {
long yMax = Math.min(r - 1, maxSum - x);
if (yMax >= 0) {
total += yMax + 1;
}
}

return total;
}
}
```

关键点说明

1. 单调栈的使用:用单调栈找到每个元素作为最值的边界
2. 处理重复元素:使用 > 和 >= 的不同组合来避免重复计算
3. 长度限制 k:在计算子数组数量时,需要限制左右扩展的总长度不超过 k
4. 数据类型:使用 long 避免整数溢出

这个解法的时间复杂度为 O(n),空间复杂度为 O(n)。

http://www.jsqmd.com/news/1088524/

相关文章:

  • AI赋能JMeter性能测试:从脚本生成到智能优化的实践指南
  • 使用JMeter对RabbitMQ进行压力测试实战指南
  • 微信数据库密钥提取:Sharp-dumpkey工具原理与实战指南
  • openeuler/pkgship高级技巧:如何利用依赖图谱优化软件包更新与删除
  • Universal x86 Tuning Utility:开源硬件调优解决方案的技术实现与应用指南
  • Three.js 模型热力图教程
  • LVGL实战指南:打造个性化嵌入式日历界面
  • 浮空全域透视动向·自愈专网直抵指挥 穿云夜视广域感知与立体管控融合指挥系统技术方案
  • Web文件上传安全:从漏洞原理到纵深防御实战指南
  • 基于STM32F407ZGT6与蓝牙的简易机械臂控制系统设计与实现
  • NCMDump:三步解锁网易云音乐加密文件,让音乐真正属于你
  • Java国密SM2集成:解决BouncyCastle“未知曲线”报错全攻略
  • Chromatic:如何像专业安全研究员一样调试和修改任意Chromium应用?
  • Blender3mfFormat插件:3D打印工作流的终极解决方案
  • 揭秘QQ聊天记录隐藏的密钥:全平台数据库解密技术深度解析
  • 从原理到代码:深入理解RSA加密算法及其Python实现
  • 盲波束成形技术与BORN算法在无线通信中的应用
  • 如何用DDrawCompat让Windows 10/11上的DirectX老游戏重获新生:技术原理与实战指南
  • [ 实战篇 ] 手把手教你激活谷歌HackBar (附疑难排查)
  • 3步打造极简高效Windows右键菜单:ContextMenuManager终极管理指南
  • Lenovo Legion Toolkit:拯救者笔记本性能调校终极指南
  • ENVI实战:从QuickBird数据到精准正射影像的完整流程
  • [特殊字符] 从零搭一个淘宝商品价格监控系统:TOP API + 定时任务 + 微信推送(附Python源码)
  • 5分钟快速上手:B站视频语音转文字工具Bili2text完整指南
  • AI模型受限发布机制与技术可信度验证
  • BetterGI安装前检查清单
  • 文件上传漏洞实战:从基础绕过到二次渲染与解析漏洞利用
  • 如何快速下载网页视频资源:猫抓浏览器扩展完整使用指南
  • 3分钟解锁网易云音乐新玩法:BetterNCM安装器终极指南
  • QMCDecode:三分钟解锁QQ音乐加密文件,让音乐真正属于你