Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务活动详情
一、题目描述
给定n
个工程师,他们每个人都有两个值,速度和效率。定义一个团队的收益为所有工程师的效率和乘以最小的工程师效率。给定一个k
,求挑选不超过k
个工程师,最多的收益为多少?
数据范围
1 <= k <= n <= 1e5
二、思路分析
朴素想法: 暴力吧,但是复杂度太高了,能不能排排序之类的?可是有两个维度的指标,要怎么处理才好呢,感觉不管搞哪个都会顾此失彼。但是我们发现,收益是由速度之和乘以最小的工程师效率,也就是说是最小的那个工程师的效率影响着团队的收益,那么我们可不可以将工程师按效率从大到小考虑?这样每一次都不用管之前哪些工程师什么效率了,反正当前一定最小,那考虑的这一堆工程师要选谁呢?肯定选最大的k个,做法也就很明显了。
不朴素想法: 按效率排序,从大到小考虑,也就是有一个“准入门槛”,在这个基础上计算所有满足这个准入门槛的最优解,也就是把符合条件的工程师排序选择速度最大的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
)