欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。
题目描述
[1705] 吃苹果的最大数目
- https://leetcode-cn.com/problems/maximum-number-of-eaten-apples/
有一棵特殊的苹果树,一连 n 天,每天都可以长出若干个苹果。在第 i 天,树上会长出 apples[i] 个苹果,这些苹果将会在 days[i] 天后(也就是说,第 i + days[i] 天时)腐烂,变得无法食用。也可能有那么几天,树上不会长出新的苹果,此时用 apples[i] == 0 且 days[i] == 0 表示。
你打算每天 最多 吃一个苹果来保证营养均衡。注意,你可以在这 n 天之后继续吃苹果。
给你两个长度为 n 的整数数组 days 和 apples ,返回你可以吃掉的苹果的最大数目。
示例 1:
输入:apples = [1,2,3,5,2], days = [3,2,1,4,2]
输出:7
解释:你可以吃掉 7 个苹果:
- 第一天,你吃掉第一天长出来的苹果。
- 第二天,你吃掉一个第二天长出来的苹果。
- 第三天,你吃掉一个第二天长出来的苹果。过了这一天,第三天长出来的苹果就已经腐烂了。
- 第四天到第七天,你吃的都是第四天长出来的苹果。
示例 2:
输入:apples = [3,0,0,0,0,2], days = [3,0,0,0,0,2]
输出:5
解释:你可以吃掉 5 个苹果:
- 第一天到第三天,你吃的都是第一天长出来的苹果。
- 第四天和第五天不吃苹果。
- 第六天和第七天,你吃的都是第六天长出来的苹果。
提示:
apples.length == n
days.length == n
1 <= n <= 2 * 10^4
0 <= apples[i], days[i] <= 2 * 10^4
只有在 apples[i] = 0 时,days[i] = 0 才成立
- 贪心
- 最小堆
题目剖析&信息挖掘
此题为模拟题,主要考查对堆的理解与应用。
解题思路
方法一 贪心+小顶堆
分析
通过对题目例子的模拟发现2个特点:
1是要先吃最近快要烂的苹果(如果有的话)。
2是最近烂的苹果所在的天数是动态的。
如果维护一个数组count,count[i]表示i+1天要烂的苹果的个数。
当我在j天时,count[i](i=>j)就是目前没有烂的,可以食用的。只要从中选取i最小的一个吃就可以。
选取最小?小顶堆好像可以解决。
还有一点是说过了n天后还可以继续吃。那我们的count数组要开到题目给出的最大规模 count[4*10^4]。过了n天后继续遍历。
思路
维护一个小顶堆存储的内容是苹果要烂的前一天。
遍历apple,
step 1 将要烂的苹果加到对应的天数上。
step 2 将天数加入小顶堆。
step 3 找到最近要烂的苹果吃一个
goto 1
再从n开始往后遍历
每次找到最近要烂的苹果吃一个。
直到堆里为空。
func eatenApples(apples []int, days []int) int {
sum := 0
count := make([]int, N)
h := &IntHeap{}
heap.Init(h)
for i, v := range apples {
if v>0 { // 当v=0时, days[i]=0, i+days[i]-1 有可能小于0
count[i+days[i]-1] += v
}
heap.Push(h, i+days[i]-1)
//将i天以前的烂苹果删除
for h.Len() > 0 && (i > (*h)[0] || count[(*h)[0]] == 0) {
heap.Pop(h)
}
// 有苹果吃一个
if h.Len() > 0 {
sum++
count[(*h)[0]]--
}
}
//n天以后继续吃
for i := len(apples); h.Len()>0; i++ {
for h.Len() > 0 && (i > (*h)[0] || count[(*h)[0]] == 0) {
heap.Pop(h)
}
if h.Len() > 0 {
sum++
count[(*h)[0]]--
}
}
return sum
}
注意
- n天后还可以继续吃苹果
- 当天烂的苹果不能吃
- apple[i] =0, days[i]=0. 要特殊处理
知识点
- 贪心
- 最小堆
复杂度
- 时间复杂度:O(nlog(n))
- 空间复杂度:O(n)
代码实现
const N = int(4e4) + 100
// An IntHeap is a min-heap of ints.
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func eatenApples(apples []int, days []int) int {
sum := 0
count := make([]int, N)
h := &IntHeap{}
heap.Init(h)
for i, v := range apples {
if v>0 {
count[i+days[i]-1] += v
}
heap.Push(h, i+days[i]-1)
for h.Len() > 0 && (i > (*h)[0] || count[(*h)[0]] == 0) {
heap.Pop(h)
}
if h.Len() > 0 {
//fmt.Println(i, sum)
sum++
count[(*h)[0]]--
}
}
for i := len(apples); h.Len()>0; i++ {
for h.Len() > 0 && (i > (*h)[0] || count[(*h)[0]] == 0) {
heap.Pop(h)
}
if h.Len() > 0 {
//fmt.Println(i, sum)
sum++
count[(*h)[0]]--
}
}
return sum
}
/*
[1,2,3,5,2]
[3,2,1,4,2]
[1,5,10,4,3,2,5]
[10,1,2,3,4,5,6]
[1]
[1]
[2]
[1]
[0,0]
[0,0]
*/
本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。

本文解析了LeetCode 1705题《吃苹果的最大数目》的解题思路,通过贪心策略结合小顶堆数据结构,指导如何在特定条件下最大化苹果食用量。讲解了关键的动态规划思想和注意事项,适合学习算法与数据结构的读者。

3366

被折叠的 条评论
为什么被折叠?



