思路:

题目的主要信息:

  • n棵苹果树上的果实存在数组a中
  • m天,每天要从每棵树上摘得苹果数存在数组b
  • 对于某一天,对于每一棵树,若是苹果树上存留得苹果数大于等于,则摘取个,否则摘取剩余得全部
  • 求每天各可以摘取共多少苹果

方法一:暴力法(超时)
具体做法:
我们可以遍历每一天,每天遍历数组a检查苹果树现有的苹果树,然后根据与b[i]的比较,摘取苹果并更新数组a。

class Solution {
public:
    vector<long> solve(vector<int>& a, vector<int>& b) {
        int n = a.size();
        int m = b.size();
        vector<long> res(m, 0);
        for(int i = 0; i < m; i++){ //遍历每天
            for(int j = 0; j < n; j++){ //遍历每棵树
                if(a[j] >= b[i]){ //若是够摘b[i]
                    a[j] -= b[i];
                    res[i] += b[i]; 
                }else{ //若是不够,则全部摘
                    res[i] += a[j];
                    a[j] = 0;
                }
            }

        }
        return res;
    }
};

复杂度分析:

  • 时间复杂度:,两层循环
  • 空间复杂度:,常数级变量,其中res是返回函数必要空间,不属于额外空间

方法二:排序+前缀和
具体做法:
对于方法一,其实当数组a中的元素归零以后便不会再用了,但是我们还是对其遍历了,因此耗费了不少时间。我们可以考虑将数组a排列成一个递增序,前面的自然先归零。我们也不用去减到零,而是使用前缀和统计数组b前面几天共摘取了,前面自然会逐渐小于前缀和。我们用j表示上一次小于前缀和的下标,可以直接用计算本次摘取数量,但是还需要循环减去本次(即j后面的)小于前缀和的苹果树,它们有多增加的数量。

class Solution {
public:
    vector<long> solve(vector<int>& a, vector<int>& b) {
        int n = a.size();
        int m = b.size();
        vector<long> res(m, 0);
        sort(a.begin(), a.end()); //排序
        long temp = 0; 
        for(int i = 0, j = 0; i < m; i++){
            temp += b[i]; //计算前缀和
            res[i] = long(n - j) * b[i]; //j前面的已经小于前缀和
            while(j < n && a[j] < temp) //减去j后面小于前缀和的
                res[i] -= temp - a[j++]; 
        }
        return res;
    }
};

复杂度分析:

  • 时间复杂度:,sort函数排序为,循环虽为两层,外循环为,内循环总共只有次,故为,舍去低次幂
  • 空间复杂度:,常数级变量,其中res是返回函数必要空间,不属于额外空间

方法三:堆+前缀和
具体做法:
上述排序的方法我们也可以用堆来实现。首先所有数组a的元素进入小顶堆,进行堆排序,然后按照上述说法计算数组b的前缀和,本轮堆中元素都是大于上一轮的前缀和,我们直接堆大小乘上b[i]即可,只不过要再减去本轮中小于前缀和的那部分,然后将其弹出堆。
图片说明

class Solution {
public:
    vector<long> solve(vector<int>& a, vector<int>& b) {
        int n = a.size();
        int m = b.size();
        vector<long> res(m, 0);
        priority_queue<int, vector<int>, greater<int>> pq; //小顶堆
        for(int i = 0; i < n; i++) //数组a入堆
            pq.push(a[i]);
        long temp = 0;
        for(int i = 0; i < m; i++){
            temp += b[i]; //前缀和
            res[i] += pq.size() * b[i]; //堆中已经数量*b[i]
            while(!pq.empty() && pq.top() < temp){
                res[i] -= temp - pq.top(); //减去多计算的
                pq.pop(); //弹出小于前缀和的
            }
        }
        return res;
    }
};

复杂度分析:

  • 时间复杂度:,堆排序为,循环虽为两层,外循环为,内循环总共只有次,故为,舍去低次幂
  • 空间复杂度:,堆空间最大长度为