1. 程式人生 > 其它 >1705 - 吃蘋果的最大數目 - 貪心 - 小頂堆

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
解釋:你可以吃掉 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]
*/


本人碼農,希望通過自己的分享,讓大家更容易學懂計算機知識。

在這裡插入圖片描述