每日一题-吃苹果的最大数量(中等)

326 阅读3分钟

题目描述:

有一棵特殊的苹果树,一连 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==napples.length == n
  • days.length==ndays.length == n
  • 1<=n<=21041 <= n <= 2 * 10^4
  • 0<=apples[i],days[i]<=21040 <= apples[i], days[i] <= 2 * 10^4
  • 只有在 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;
    }
}

题目链接