1705 - 吃蘋果的最大數目 - 貪心 - 小頂堆
技術標籤:leetcodeleetcode資料結構貪心演算法堆排序
歡迎關注更多精彩
關注我,學習常用演算法與資料結構,一題多解,降維打擊。
文章目錄
題目描述
[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 個蘋果:
- 第一天到第三天,你吃的都是第一天長出來的蘋果。
- 第四天和第五天不吃蘋果。
- 第六天和第七天,你吃的都是第六天長出來的蘋果。
提示:
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個特點:
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]
*/
本人碼農,希望通過自己的分享,讓大家更容易學懂計算機知識。