【LeetCode 1383】Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务

92 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务活动详情

一、题目描述

给定n个工程师,他们每个人都有两个值,速度和效率。定义一个团队的收益为所有工程师的效率和乘以最小的工程师效率。给定一个k,求挑选不超过k个工程师,最多的收益为多少?

数据范围

1 <= k <= n <= 1e5

二、思路分析

朴素想法: 暴力吧,但是复杂度太高了,能不能排排序之类的?可是有两个维度的指标,要怎么处理才好呢,感觉不管搞哪个都会顾此失彼。但是我们发现,收益是由速度之和乘以最小的工程师效率,也就是说是最小的那个工程师的效率影响着团队的收益,那么我们可不可以将工程师按效率从大到小考虑?这样每一次都不用管之前哪些工程师什么效率了,反正当前一定最小,那考虑的这一堆工程师要选谁呢?肯定选最大的k个,做法也就很明显了。

image.png

不朴素想法: 按效率排序,从大到小考虑,也就是有一个“准入门槛”,在这个基础上计算所有满足这个准入门槛的最优解,也就是把符合条件的工程师排序选择速度最大的k个。这里可以用堆来做,这样每次新加入一个工程师只要 logn 的复杂度就可以了。维护堆的数量在k就行。

这样子做的正确性在于,对于每一个效率e[i],我们都能计算出对应的最大收益,而不会漏过哪些答案,最后输出个最大值就可以了。而最大收益的计算我们用堆在维护着。

三、AC代码

class Solution {
public:
    long long now=0,tot=0;
    long long ans=0;
    vector<pair<int, int> > q;

    static bool cmp(pair<int,int > aa, pair<int,int > bb) {
        if (aa.second == bb.second) return aa.first > bb.first;
        return aa.second > bb.second;
    }

    priority_queue<int , vector<int>, greater<int> > que;
    int maxPerformance(int n, vector<int>& speed, vector<int>& efficiency, int k) {
        for (int i=0; i<n; i++) {
            q.push_back(make_pair(speed[i], efficiency[i]));
        }

        sort(q.begin(), q.end(), cmp);
        for (int i=0; i<n; i++) {
            long long minn = q[i].second;
            int sp = q[i].first;
            que.push(sp);
            now += sp;
            tot++;
            if (tot>k) {
                now-=que.top();
                que.pop();
                tot--;
            }
            ans = max(ans, minn*now);
        }
        return ans%((long long)(1e9+7));
    }
};

四、总结

虽然题目有两个维度的指标,但是我们发现可以只考虑某一个依次计算,而另一个想办法进行优化()来降低复杂度。关键点在于怎么找到两个维度之间的相互组织关系(一种设计),使得我们可以借用“依次计算”的上一次的结果,优化复杂度(logn