题目描述:
题目链接:力扣
有一棵特殊的苹果树,一连 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 个苹果:
- 第一天,你吃掉第一天长出来的苹果。
- 第二天,你吃掉一个第二天长出来的苹果。
- 第三天,你吃掉一个第二天长出来的苹果。过了这一天,第三天长出来的苹果就已经腐烂了。
- 第四天到第七天,你吃的都是第四天长出来的苹果。
题目分析:
1.首先根据题目信息,可以在n天之后继续吃苹果,可以分为两种情况分别讨论,n天之前,n天之后。
2.然后根据贪心思路,只要每次吃的都是腐烂日期最早的苹果那么一定能吃到最多的苹果,但是需要注意,腐烂日期一定要大于等于当前日期 i 。
3.根据前两点分析,我们需要维护一个优先队列queue存储第i天苹果的个数和腐烂日期(用一个数组表示int[] temp=new int[2], temp[0] 表示腐烂日期,temp[1]表示苹果数量),然后根据腐烂日期进行递增排序,进行一次for(int i=0;i<n;i++)循环,每次循环代表一天,每天需要将已经腐烂的苹果处理掉,也就是将腐烂日期小于 i 的元素出队;将当天生产的苹果放入优先队列,队首元素temp[1] -1 表示已经吃过一个苹果。
4.n天之后已经不生产苹果了,所以每次进行队首元素判断,ans(代表吃掉的苹果数)+=Math.min(queue.peek()[0]-n+1,queue.peek()[1]);队首这一批苹果一定是腐烂日期最小的,此时有两种情况:
1)队首元素苹果数量很多,每天吃一个,直到这一批苹果的腐烂日期也还没吃完,所以只能吃:(当前苹果腐烂日期)queue.peek()[0] - (上一批苹果吃完的日期)n+1;
2)队首元素苹果数量很少,每天吃一个,没有到苹果腐烂日期就吃完了,所以只能吃:queue.peek()[1]
代码(JAVA版本)
public int eatenApples(int[] apples, int[] days) {
//1.首先维护一个优先队列,队列元素是一维数组,根据腐烂日期排序
PriorityQueue<int[]> queue=new PriorityQueue<>((a,b)->a[0]-b[0]);
//2.for循环模拟每天吃苹果过程(n天前)
int n=apples.length,ans=0;
for(int i=0;i<n;i++){
//2.1每天吃苹果之前先去除掉腐烂的苹果
while(!queue.isEmpty()&&queue.peek()[0]<i)queue.poll();
//2.2加入当天生产的苹果(如果当天有苹果)日期从0开始算
if(apples[i]>0){
queue.add(new int[]{i+days[i]-1,apples[i]});
}
//2.3吃苹果,注意一定要判断队列是否为空
if(!queue.isEmpty()){
//吃苹果
queue.peek()[1]--;
ans++;
//如果苹果吃完,就要从队列中取出
if(queue.peek()[1]==0)
queue.poll();
}
}
//3.n天之后,第一次循环开始时n等于apples.length,但是之后需要更新为上次吃苹果的日期
while(!queue.isEmpty()){
//3.1处理掉腐烂的苹果
while(!queue.isEmpty()&&queue.peek()[0]<n){
queue.poll();
}
//3.2处理腐烂时间最小的一批苹果,注意一定判断队列是否为空
if(!queue.isEmpty()){
int[] temp=queue.poll();
//这批苹果数量
int num=temp[1];
//这批苹果的腐烂日期
int rotdays=temp[0];
ans+=Math.min(num,temp[0]-n+1);
n+=Math.min(num,temp[0]-n+1);
}
}
return ans;
}