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

千问 LeetCode 2926. 平衡子序列的最大和 Java实现

这是一道经典的动态规划(DP)结合离散化与树状数组(或线段树)优化的题目。

💡 核心解题思路

1. 题目转化:
题目要求平衡子序列满足 nums[ij] - nums[ij-1] >= ij - ij-1。
我们将不等式移项变形,得到 nums[ij] - ij >= nums[ij-1] - (ij-1)。
如果我们定义一个新数组 b[i] = nums[i] - i,那么题目就转化为:在原数组中找一个下标递增的子序列,使得对应的 b[i] 也是非递减的,并且原数组 nums 的元素和最大。

2. 动态规划定义:
设 dp[i] 表示以 nums[i] 结尾的平衡子序列的最大和。
状态转移方程为:dp[i] = max(0, max{dp[j]}) + nums[i],其中 j < i 且 b[j] <= b[i]。
(max(0, ...) 是因为如果前面的最大和是负数,我们不如直接从 nums[i] 重新开始)。

3. 数据结构优化:
如果直接暴力遍历 j,时间复杂度是 O(N^2),会超时。我们需要一种数据结构来快速求出所有满足 b[j] <= b[i] 的 dp[j] 中的最大值。
这正好是树状数组(Binary Indexed Tree, BIT)或线段树的强项,它们可以在 O(logN) 的时间内完成“前缀最大值查询”和“单点更新”。

4. 离散化:
由于 nums[i] 的范围很大(-10^9 到 10^9),b[i] 的值域也非常大,不能直接作为树状数组的下标。因此,我们需要先对 b[i] 进行离散化(排序并去重,映射到 1 到 N 的排名)。

💻 Java 代码实现

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
public long maxBalancedSubsequenceSum(int[] nums) {
int n = nums.length;

// 1. 预处理 b[i] = nums[i] - i,并进行离散化
int[] b = new int[n];
for (int i = 0; i < n; i++) {
b[i] = nums[i] - i;
}

// 复制一份 b 数组用于排序去重
int[] sortedB = b.clone();
Arrays.sort(sortedB);
// 去重
int m = 1;
for (int i = 1; i < n; i++) {
if (sortedB[i] != sortedB[i - 1]) {
sortedB[m++] = sortedB[i];
}
}

// 2. 初始化树状数组(用于维护前缀最大值)
// 树状数组下标从 1 开始,所以大小为 m + 1
long[] bit = new long[m + 1];
// 初始化为极小值
Arrays.fill(bit, Long.MIN_VALUE);

long globalMax = Long.MIN_VALUE;

// 3. 遍历原数组,进行状态转移
for (int i = 0; i < n; i++) {
// 二分查找当前 b[i] 在离散化数组中的排名(从 1 开始)
int rank = Arrays.binarySearch(sortedB, 0, m, b[i]) + 1;

// 查询所有 b[j] <= b[i] 的最大 dp[j](即树状数组的前缀最大值)
long prevMax = query(bit, rank);

// 状态转移:如果前面的最大和是负数,不如从当前 nums[i] 重新开始
long currentDp = Math.max(0L, prevMax) + nums[i];

// 更新全局最大值
globalMax = Math.max(globalMax, currentDp);

// 将当前 dp 值更新到树状数组中
update(bit, rank, currentDp);
}

return globalMax;
}

// 树状数组查询前缀最大值:查询 [1, idx] 范围内的最大值
private long query(long[] bit, int idx) {
long max = Long.MIN_VALUE;
while (idx > 0) {
max = Math.max(max, bit[idx]);
idx -= idx & -idx;
}
return max;
}

// 树状数组单点更新:将 idx 位置的值更新为 max(bit[idx], val)
private void update(long[] bit, int idx, long val) {
while (idx < bit.length) {
bit[idx] = Math.max(bit[idx], val);
idx += idx & -idx;
}
}
}

📊 复杂度分析
* 时间复杂度:O(N log N)。排序离散化需要 O(N log N),遍历数组时,每次二分查找、树状数组的查询和更新都是 O(log N),总时间复杂度为 O(N log N)。
* 空间复杂度:O(N)。需要额外的数组存储离散化后的 b 以及树状数组。

⚠️ 易错点提示
1. 数据溢出:题目中 nums[i] 的和可能会超过 int 的范围,因此 dp 状态、树状数组以及最终结果必须使用 long 类型。
2. 负数处理:树状数组初始值需要设为 Long.MIN_VALUE。在状态转移时,一定要和 0 取最大值(Math.max(0L, prevMax)),因为如果前面的最优解是负数,我们完全可以舍弃前面的部分,只保留当前的 nums[i]。
3. 离散化去重:在排序后一定要进行去重操作,否则树状数组的大小会超出预期,且二分查找的排名会出错。

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

相关文章:

  • 麒麟V10服务器上,毕昇JDK 1.8缺失javafx.util.Pair的快速修复指南
  • 告别C语言!用Python玩转智能车:NXP RT1021核心板+MicroPython保姆级入门指南
  • PyTorch-NPU/baichuan2_7b_base模型蒸馏技术:如何从小模型获得大模型性能
  • SAP后台配置保姆级指南:从SPRO入口到生产环境传请求,新手避坑全流程
  • 数字媒体真实性验证实战指南:从元数据到AI检测的完整工具箱
  • Campus-iMaoTai:基于Spring Boot的茅台预约自动化系统架构设计与实现
  • DeepSeek Coder 33B Instruct常见问题解决:从安装错误到推理异常的完整排查指南
  • 2026年评价高的给排水涂塑钢管/内外涂塑钢管优质供应商推荐 - 行业平台推荐
  • 如何永久保存微信聊天记录:3步掌握WeChatMsg数据备份终极指南
  • 如何用微信聊天记录打造你的专属AI记忆库:留痕项目完全指南
  • 微软翻译技术演进:从统计机器翻译到深度神经网络的服务化实践
  • SPACER求解器:Z3中模型检测与定理证明融合的程序验证引擎
  • 2026年口碑好的广东纱窗执手/平开窗执手/广东门窗执手厂家选择推荐 - 品牌宣传支持者
  • 2019数模国赛B题‘同心协力’一等奖方案:可修改论文+Matlab与Lingo双平台源码
  • 2026年口碑好的法兰连接涂塑钢管/消防涂塑钢管/矿用双抗涂塑复合钢管/内外涂塑钢管推荐品牌厂家 - 品牌宣传支持者
  • cyrillic_PP-OCRv5_mobile_rec_safetensors完全解析:从模型架构到实战应用
  • 2026武汉配眼镜推荐,写字楼商场眼镜城渠道价差揭秘,同款能差一倍 - 配眼镜新资讯
  • 微信小程序原生2048游戏源码,带完整页面+逻辑+资源,开箱即调
  • Lance图像理解能力实测:视觉问答与推理任务最佳实践指南
  • 2026年知名的广东七字执手/平开窗执手/执手批量采购厂家推荐 - 行业平台推荐
  • STM32F103C8T6用HAL库驱动74HC595,点亮三位数码管(附Proteus仿真文件)
  • 高效研究周报系统:从知识管理到团队协同的工程实践
  • 2026武汉配眼镜推荐,进出空调房镜片一片雾,五家店防雾方案实测 - 配眼镜新资讯
  • 从SPI时序到数据解析:深入理解AS5047P磁性编码器的通信协议
  • OrCAD原理图端口用对了吗?从Place Port到Off-Page Connector,一篇讲清区别、选用与高效转换技巧
  • 女性机器学习工作坊十年:从社群构建到技术多样性实践
  • 告别手动剪辑:5分钟学会用AI智能剪辑你的视频内容
  • 2026年比较好的膜结构看台/膜结构景观源头工厂推荐 - 行业平台推荐
  • 深度解析Listen1音乐扩展:从性能瓶颈到极致优化的实战指南
  • 3分钟搞定黑苹果配置:OpCore Simplify图形化工具完全指南