1705 - 吃苹果的最大数目 - 贪心 - 小顶堆

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

欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。

题目描述

[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 才成立

Related Topics
  • 贪心
  • 最小堆

题目剖析&信息挖掘

此题为模拟题,主要考查对堆的理解与应用。

解题思路

方法一 贪心+小顶堆

分析

通过对题目例子的模拟发现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]
*/


本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值