LeetCode 1553. 吃掉 N 个橘子的最少天数
题目描述


题意分析
每天只能选择一种吃法:吃一个;数量能被
2整除时吃掉一半;能被3整除时吃掉三分之二。后两种操作分别让剩余数量变成原来的二分之一、三分之一,要求吃完全部橘子的最少天数。
n可达二十亿,逐个求出0..n的答案太慢。批量吃法能快速缩小规模,关键是把它之前连续“每天吃一个”的过程压缩掉。
解法:记忆化搜索
核心思路
[!blue]
定义
f(n)为吃完剩余n个橘子的最少天数。若下一次批量操作是除以2,先用n % 2天吃到可整除,再花一天执行批量操作,剩下n / 2个;总天数是n % 2 + 1 + f(n / 2)。除以3的候选同理,取两者较小值。只吃掉余数就立刻执行批量操作,不会漏掉更优方案。设除以
k前原本准备先吃r + k*t个,其中r = n % k。改成先吃r个、除以k、再逐个吃t个,会到达同样的剩余数量,却少花(k - 1)*t天。因此没必要在第一次批量操作前多吃整整一组k个。全程只逐个吃也不会更优:当数量至少为
2时,先调整到偶数并批量吃一次,再逐个吃完,天数已经不超过原来的逐个吃法。所以只比较这两类批量转移即可;0、1个橘子直接返回数量。两条分支可能到达相同剩余数量,而后续最优天数只由这个数量决定,用哈希表缓存
f(n)。每次递归都把规模缩到一半或三分之一,避免展开所有中间数量。
解题步骤
- 剩余数量不超过
1时,直接返回它所需的天数。- 若缓存中已有当前数量的答案,直接复用。
- 分别计算“调整为偶数 + 一次二分操作 + 剩余最优值”和“调整为三的倍数 + 一次三分操作 + 剩余最优值”。
- 把两个候选的较小值存入缓存,再返回。
代码实现
class Solution {
private final Map<Integer, Integer> memo = new HashMap<>();
public int minDays(int n) {
if (n <= 1) {
return n;
}
Integer cached = memo.get(n);
if (cached != null) {
return cached;
}
// 余数次单个删除,加一次除法操作,再求剩余数量的最优值。
int byTwo = n % 2 + 1 + minDays(n / 2);
int byThree = n % 3 + 1 + minDays(n / 3);
int answer = Math.min(byTwo, byThree);
memo.put(n, answer);
return answer;
}
}
func minDays(n int) int {
memo := make(map[int]int)
var dfs func(int) int
dfs = func(oranges int) int {
if oranges <= 1 {
return oranges
}
if cached, exists := memo[oranges]; exists {
return cached
}
// 余数次单个删除,加一次除法操作,再求剩余数量的最优值。
byTwo := oranges%2 + 1 + dfs(oranges/2)
byThree := oranges%3 + 1 + dfs(oranges/3)
answer := byTwo
if byThree < answer {
answer = byThree
}
memo[oranges] = answer
return answer
}
return dfs(n)
}
复杂度分析
- 时间复杂度:新实例单次求解期望 $O(\log^2(n+1))$。递归状态可写成 $\lfloor n/(2^a3^b)\rfloor$,两种除法次数各为对数量级,每个状态只计算一次。
- 空间复杂度:单次缓存为 $O(\log^2(n+1))$,递归栈为 $O(\log(n+1))$。Java 跨调用复用成员缓存时,空间按累计不同状态数计算。
关键点总结
[!green]
- 一次转移包括余数调整、批量操作本身、剩余数量的最优解,三部分都要计入天数。
- 交换操作顺序证明了只需调整余数,不必枚举任意多次逐个吃。
- 缓存的键是剩余数量,走到该状态之前花了几天不会影响后续最优值。
易错点总结
[!yellow]
- 不能只比较
f(n / 2)、f(n / 3),调整到可整除以及执行操作本身都需要时间。- 当前不能整除时也要考虑批量分支,先吃掉余数就能使用它。
0和1必须作为终止状态;剩余2个时,统一公式会由二分分支得到最少的2天。- 直接加入
f(n - 1)并递归展开,会重新引入大量连续状态,失去余数压缩的作用。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 397. 整数替换 | 中等 | 同样通过先调整到可整除状态再批量缩小整数,避免逐个减少的线性搜索。 |
| 991. 坏了的计算器 | 中等 | 同样可以反向或按大步操作建立更小状态,本题还允许除3对应的批量吃法。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!