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

Qwen模型 Max LeetCode 2790. 长度递增组的最大数目 Java实现

哎呀,这道 LeetCode 2790 题「长度递增组的最大数目」挺有意思的!我刚刷完不久,来和你一起理清楚思路 😄

题目大意是:给你一个正整数数组 `usageLimits`,你要用这些数字作为“使用次数上限”,构造尽可能多的**非空组**,使得第 i 个组的大小严格大于第 i-1 个组(即组的长度严格递增)。每个元素最多只能被用 `usageLimits[i]` 次。

目标是求出最多能构造多少个这样的组。

---

### 💡核心思想:贪心 + 二分 or 贪心模拟

其实这题有个很巧妙的贪心策略:

> **为了构造最多的组,我们应该让前面的组尽可能小(比如第1组大小为1,第2组为2,…,第k组为k),这样总消耗最少,更容易满足限制。**

所以问题转化为:**最大的 k 是多少,使得我们可以用给定的 usageLimits 构造出 k 个组,第 i 组大小为 i(i 从 1 到 k)?**

总需求人数 = 1 + 2 + … + k = k*(k+1)/2

但不能直接看总和!因为每个 usageLimits[i] 是独立的“资源池”,不能随便拼凑。比如 [1,1,1] 总和是3,但无法组成大小为2的组(需要2个不同元素各至少1次,但每个只能用1次,可以;但若要组大小为3,就需要3个元素各至少1次——刚好可以。但如果 usageLimits=[3],总和是3,却只能组成1个组(因为只有一个元素,无法组成大小>1的组,因为组内元素必须来自不同下标?等等!)

⚠️ 注意:**组内的元素可以重复使用同一个下标的元素吗?**

答案是:**不可以!**
LeetCode 官方说明:每个组由若干**不同下标的元素**组成(即每组中每个元素来自不同的位置),但同一个下标可以在多个组中出现,只要不超过 usageLimits[i]。

换句话说:
- 每个组是一个集合(无重复下标)
- 第 j 个组的大小 = 该组包含的下标数量
- 下标 i 最多出现在 usageLimits[i] 个组中

所以问题等价于:
> 有 n 个“工人”,每个工人 i 最多能参与 usageLimits[i] 个项目。现在要安排 k 个项目,第1个项目需要1个工人,第2个需要2个……第k个需要k个工人。问最多能安排多少个项目?

这就清晰多了!

---

### ✅ 解法:贪心 + 排序 + 累积检查

我们可以尝试从小到大构造组(1,2,3,...),每次尽量用“还能干活”的工人。

但更高效的做法是:

1. 将 `usageLimits` 从大到小排序(或者从小到大?我们试试逻辑)
2. 实际上,**我们希望高 usage 的元素去支撑大的组**,但贪心策略是:**把 usageLimits 从小到大排序,然后逐步累积“可用资源”**,看是否能满足前 k 个组的累计需求。

不过更标准的做法是:

> 对 usageLimits 降序排序,然后模拟:维护当前已分配的组数 k,遍历每个 usage,尽可能多地“贡献”给后面的组。

但其实有一个经典解法是:

#### 🌟 方法:排序 + 贪心累加(推荐)

```java
import java.util.*;

public class Solution {
public int maxIncreasingGroups(List<Integer> usageLimits) {
// 转成数组并降序排序(也可以升序,但下面逻辑用升序+逆序处理)
int[] limits = usageLimits.stream().mapToInt(i -> i).toArray();
Arrays.sort(limits); // 升序

long total = 0;
int groups = 0;

// 从最大的 usage 开始用?不,我们从前往后,但逻辑是:
// 每当我们有足够的“累积资源”来满足下一个 group 的 size (groups+1),就增加一组
for (int i = limits.length - 1; i >= 0; i--) {
total += limits[i];
// 检查是否能组成 groups+1 个组(第 groups+1 个组大小为 groups+1)
// 前 groups+1 个组总共需要 (groups+1)*(groups+2)/2 个人次
if (total >= (long)(groups + 1) * (groups + 2) / 2) {
groups++;
}
}
return groups;
}
}
```

等等,这个逻辑对吗?

其实更常见且正确的做法是 **升序排序后从前向后遍历**,但用“贪心填充”的思想:

#### ✅ 正确且简洁的解法(官方推荐思路):

```java
class Solution {
public int maxIncreasingGroups(List<Integer> usageLimits) {
Collections.sort(usageLimits);
long total = 0;
int groups = 0;

for (int limit : usageLimits) {
total += limit;
// 如果当前累积资源足够支撑 groups+1 个组(总需求 = 1+2+...+(groups+1))
if (total >= (long)(groups + 1) * (groups + 2) / 2) {
groups++;
}
}
return groups;
}
}
```

💡 为什么升序排序后从前向后是对的?

**解释**:
虽然大 usage 的元素更有价值,但我们并不需要知道谁干了什么,只需要总“人次”是否足够覆盖前 k 个组的总需求。而由于组的大小递增,越往后需求越大,所以我们希望尽早积累资源。

但关键点在于:**只要总可用人次 ≥ 1+2+...+k,并且我们有至少 k 个元素(因为第 k 组需要 k 个不同下标)** —— 等等,真的需要至少 k 个元素吗?

比如 usageLimits = [5],只有一个元素,能组成几个组?
- 第1组:需要1个不同下标 → 可以(用这个元素)
- 第2组:需要2个不同下标 → 不行!因为只有一个下标

啊!这里有个隐藏条件:**第 g 个组的大小是 g,意味着需要 g 个不同的下标**。因此,**最多只能有 n 个组**(n = usageLimits.size()),因为第 n+1 组需要 n+1 个不同下标,但我们只有 n 个。

但上面的代码没考虑这点?其实自动满足了,因为循环最多 n 次,groups 最多增加 n 次。

更重要的是:**即使总人次够,但如果元素个数不够,也无法组成大组**。但因为我们是从前往后遍历所有元素,每一步都代表“现在有 i+1 个可用下标”,所以当 groups 增加到超过当前可用下标数时,其实不会发生,因为:

比如 n=1,usage=[5]
- i=0: total=5
- 检查:5 >= 1*2/2 = 1 → groups=1
- 下一次想 groups=2,需要 total >= 3,虽然 5>=3,但此时我们只有一个元素,无法组成大小为2的组!

❌ 所以上面的代码在 [5] 输入下会错误返回 2 吗?

我们来验证:

输入:[5]
- groups=0, total=0
- limit=5 → total=5
- 检查:5 >= (0+1)*(0+2)/2 = 1 → groups=1
- 循环结束,返回 1 ✅ 正确!

再试 [1,1,1]:
- sorted: [1,1,1]
- i=0: total=1 → 1 >= 1? yes → groups=1
- i=1: total=2 → need (2*3/2)=3? 2<3 → no
- i=2: total=3 → 3>=3 → groups=2
- 返回 2

但 [1,1,1] 能组成2个组吗?
- 组1:大小1 → 用任意一个
- 组2:大小2 → 需要两个不同下标,各用一次 → 可以(比如用下标0和1)
- 元素使用情况:[1,1,0] → 合法 ✅

再试 [1,1]:
- total=1 → groups=1
- total=2 → need 3? no → groups=1
- 正确,因为无法组成大小为2的组(需要2个元素,但总人次2,刚好够,但第2组需要2人,第1组需要1人,共需3人次?不!

等等,这里容易混淆:

- 第1组:1人
- 第2组:2人
- 总共需要 1+2 = 3 人次
- [1,1] 总人次=2 < 3 → 无法组成2组 → 正确

所以算法是对的!

### 🎯 为什么这个贪心成立?

关键洞察来自 **Hall's Marriage Theorem** 或者更简单的:
如果我们把 usageLimits 排序后,按顺序分配,那么只要在前 i 个元素(即最小的 i 个)提供的总资源 ≥ 前 k 个组中“落在前 i 个元素上的最小需求”,就能构造。

但简化后,**全局总人次 ≥ 1+2+...+k 是必要条件,结合排序后逐步检查,它也是充分的**。这是因为我们可以总是把大的 usage 留给后面的大组,而排序后从小到大处理,相当于“预留”了更大的 usage 给未来。

实际上,这个解法已被 LeetCode 官方题解和大量高赞解答采用。

---

### ✅ 最终 Java 代码(简洁版)

```java
class Solution {
public int maxIncreasingGroups(List<Integer> usageLimits) {
Collections.sort(usageLimits);
long total = 0;
int groups = 0;
for (int limit : usageLimits) {
total += limit;
if (total >= (long) (groups + 1) * (groups + 2) / 2) {
groups++;
}
}
return groups;
}
}
```

时间复杂度:O(n log n)(排序)
空间复杂度:O(1)

---

如果你正在准备面试,这题考察的是**贪心思维 + 数学建模能力**,能把“组大小递增”转化为“总需求公式”是关键!

要不要我再举个例子帮你巩固一下?或者你有其他 LeetCode 题想一起讨论?😄

http://www.gsyq.cn/news/1409533.html

相关文章:

  • 中小企业本地化RAG一体机实测:从“文档杂乱”到“5秒溯源”,一个开箱即用的工程方案
  • 今天没爆款,但 `claude-mem` 这个新面孔一天涨了 352 星,给 Claude Code 装上记忆
  • CPU上LLM服务优化:Sandwich架构解决预填充与解码阶段挑战
  • 有哪些AI写作辅助软件是真的懂学术语言,而不是胡乱堆砌?
  • 全局/静态区的变量在程序中的生命周期是如何确定的?
  • CICV2026|51Sim分享面向物理AI的下一代仿真体系
  • 5分钟彻底解决机械键盘连击问题:免费开源防抖工具终极指南
  • FP7125停产断供?替代物料FP7135详解来了
  • GMS 1.4 YYC编译的游戏,如何安全地修改里面的文字和图片?(附UndertaleModTool实战)
  • 别再只看Top-1了!用Python代码实战解析Rank-1与Rank-5正确率,帮你更懂模型真实能力
  • Vue项目里用Highcharts+Canvas画频谱瀑布图,30ms刷新也不卡(附完整代码)
  • 孜喵鳕鱼泡芙真的有母婴博主测评过吗?结果怎么样?值不值得买?
  • UE4玻璃和水面材质实战:从折射率到光照模式,手把手调出真实半透明效果
  • 百度文心助手 LeetCode 2751. 机器人碰撞 C语言实现
  • 基于可靠性的直接Turbo译码器RCODD的FPGA实现与优化
  • 2026年零基础适配!新手友好型AI自动化测试工具测评
  • 技术笔记 | 解析SQR-PR300管道机器人
  • ChatGPT驱动的客户旅程地图重构:从模糊感知到精准预测的7步落地框架
  • 天龙八部单机版GM工具终极指南:5分钟快速掌握游戏数据管理
  • 2026 AR 巡检标杆实录
  • ANSYS Workbench螺栓连接仿真避坑指南:从Beam连接到预紧力锁死,一个案例讲透
  • 从CentOS 8.5 Minimal到开发环境:安装后必做的10件事(配置yum源、SSH、防火墙)
  • 观察使用Taotoken的Token Plan套餐后月度账单的变化
  • 多级重叠Schwarz预处理技术在CFD中的应用与优化
  • 基于 HarmonyOS 6.0 的日程备忘应用页面构建:深色主题与数据看板设计详解
  • ManySpeech-CLI:开箱即用的本地命令行语音识别工具
  • Linux内核开发者视角:深入SMMUv3驱动,手把手拆解dma_map_sg()的IOVA连续映射魔法
  • 力扣HOT100(35)回溯-全排列
  • 国产第一!Qwen3.7-Max全端上线,好易智算同步首发,企业级Agent底座再添新选择
  • 阿姆智创IBOX-6076R工控一体机,机器视觉设备控制升级