题目描述:
有一棵特殊的苹果树,一连 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[i] = 0 时,days[i] = 0 才成立
分析:
题目要求求取吃苹果的最大数量,而苹果都会过期,那么显而易见,为了最大限度的保证我能尽可能更多天数的吃到苹果,我只能在即将腐败的苹果中挑一个吃了。很明显的贪心策略。接下来,我们模拟吃苹果。我们用一个二元组[m,n](其中,m表示改天产出中苹果中剩余苹果数,n表示该批苹果过期的天数)。拿示例1举例:
第一天:i = 0
{ [0,3] }:产出1个苹果被吃了,苹果数为 0,直接出队。
第二天:i = 1
{ [1,1+2] }:产出2个,吃一个,剩余一个,第3天过期。
第三天:i = 2
{ [0,3], [3,2+1] }:吃的为第二天产出的苹果,苹果数为0出队。当天产出3个,第3天后过期。
第三天:i=3
{[2,3], [5,3+4]}:吃的为第三天产出的苹果,苹果要过期了出队。当天产出5个,第7天后过期。
... 依次类推
所以我们需要二元组来表示苹果状态,一个优先队列来存储这些二元组。
时间复杂度分析:
O(nlogn),其中 n 是数组 apples 和 days 的长度。优先队列中最多有 n 个元素,每个元素加入优先队列和从优先队列中取出各一次,每次操作的时间复杂度是 O(logn),因此总时间复杂度是 O(nlogn)。
编码:
class Solution {
public int eatenApples(int[]apples, int[] days){
PriorityQueue<int[]> queue = new PriorityQueue<>((a, b) -> a[1] - b[1]);
int ans = 0;
//阶段一,树上还长苹果的时候
for (int i = 0; i < apples.length; i++) {
if (apples[i] > 0) {
queue.add(new int[]{apples[i] , i + days[i]});
}
//清理过期苹果
while(queue.size() > 0) {
int[] top = queue.peek();
if (top[1] <= i){
queue.poll();
} else {
break;
}
}
//吃苹果
if (queue.size() > 0) {
int[] top = queue.poll();
top[0]--;
ans++;
if (top[0] > 0) {
queue.add(top);
}
}
}
//阶段二,清库存
int length = apples.length;
while (queue.size() > 0) {
int a[] = queue.poll();
ans += Math.min(a[0], a[1] - length);
length += Math.min(a[0], a[1] - length);
}
return ans;
}
}
大佬的写法:
class Solution {
public int eatenApples(int[] apples, int[] days) {
int lastDay = 0;
for (int i = 0; i < apples.length; i++) {
lastDay = Math.max(lastDay, i + days[i] - 1);
}
int[] rotDay = new int[lastDay + 1];
int ans = 0, start = lastDay, end = 0;
for (int i = 0; i < lastDay + 1; i++) {
if (i < apples.length && apples[i] > 0) {
rotDay[i + days[i] - 1] = apples[i];
start = Math.min(start, i + days[i] - 1);
end = Math.max(end, i + days[i] - 1);
}
start = Math.max(start, i);
for (; start <= end; start++) {
if (rotDay[start] > 0) {
ans++;
rotDay[start]--;
break;
}
}
}
return ans;
}
}