hi,你们好,我是汉堡!我真的是好久没发了我去发B站了,我又回来了!今天我们来看一下 力扣第 3767题 选择K个任务的最大总分 本题是一道贪心算法题目 难度中等。本篇题解完全按照小白的思考逻辑进行思考,告诉你如何从一个初期的不完整的思路到最后优化过的正确思路。希望你能认真看完并仔细跟随我的思路思考
我这一篇写的是真不容易啊!
文章目录
题目链接
没有力扣账号的注册一下
思路分析
前置分析
学过贪心的一读题就知道这是一道贪心算法的题目 其实可以查看题目的标签
我们知道:想要获得最大的总分,就要使每一次做任务时获得的分数最大(贪心的基本思想:局部最优造就全局最优 前面的c++算法------贪心策略幽默详细讲解 原来贪婪也有作用(习题持续更新)讲过)而想要每一次获得更大的分数
- 如果第
i个任务使用技巧 1 完成,你将获得technique1[i]分。- 如果使用技巧 2 完成,你将获得
technique2[i]分。
根据题目的描述,想要获得最优的答案,我们要使用能获得最大分数的技巧来完成任务,简单的来说:每次完成任务,选择能获得最大分数的技巧
初步考虑
但是,你接着往下读
你 必须 使用技巧 1 完成 至少
k个任务(不需要是前k个任务)
这时候你蒙了:如果每次选择能获得最大分数的技巧,那么不一定能够使用够k次技巧 1 1 1 ,而如果先选择够k次技巧 1 1 1剩下的再按照前面的规则选择,你又不知道前面的k个** 技巧 1 1 1** 选择哪k个才能使最后获得最大的总分
浅表的想,你想到可以选择前k个最大的technique1中的元素,这样应该可以使这k个任务获得最大的分值
but(但是),我告诉你:不对!万一有那么一组数据,前k个最大的technique1元素对应的 technique2 的元素比这k个还要大,而稍微小一点的k个technique1元素对应的 technique2 的元素比它要小(它指稍微小一点的一些technique1元素 )这时候明显选的前k个元素不符合得到最优答案的条件,而又有一种选法(刚说的)既满足题目的要求有满足获得最优答案的要求,这说明这种选法不是最优的,这个策略行不通!
深入分析
我们继续换一种思路,在刚刚举反例的过程中,我们想到了只需要选择 技巧 1 − 技巧 2 技巧1-技巧2 技巧1−技巧2的差最大的k个技巧 1 1 1即可,剩下的再按照基本原则进行选择
举个题目中的例子分析一下 :
technique1 = [5,2,10], technique2 = [10,3,8], k = 2
先求出所有的technique1[i]-technique2[i],为:[-5,-1,2]
再降序排列得到:[2,-1,-5]
然后选择前k个即可,就是:[2,-1] 他们两个对应的是 technique1中 的:[10,2]
最后按照基本的原则选择剩下的,就是 technique1中的5和technique2中的10 这一对,显然选择10。
得到答案 10 + 2 + 10 = 22 10+2+10=22 10+2+10=22 答案正确
疑惑解释
但是,认真思考的你提出了问题:"如果technique1[i] - technique2[i] 三个都是 -3,而k=2,随便选了两个-3,结果这两个负三对应的technique1[i]很小, technique2[i]很大,完美避开了最优,最大的分数啊!"
你是一个认真思考的人,很棒!但是你的这个质疑不对因为你怀疑我的缜密思考了!,我在想这个思路时也想到了这个问题,但是仔细一想其实不存在。
举个例子:
technique1 = [1,2,3], technique2 = [4,5,6], k = 2
这个例子就是你的"反例",我们来试一下
如果选任务1和2用技巧1,则任务3用技巧2,总分 = 1 + 2 + 6 = 9 1+2+6 = 9 1+2+6=9
如果选任务1和3,总分 = 1 + 3 + 5 = 9 1+3+5 = 9 1+3+5=9
如果选任务2和3,总分 = 2 + 3 + 4 = 9 2+3+4 = 9 2+3+4=9
答案不管如何选前k个都相同。
为啥呢?我解释一下道理:因为他们的差都相同,所以
选technique1小一点的,最后的technique2会刚好补上,
选technique1大一点的,最后的technique2会刚好拉低。
最后的答案还是一样的
代码实现
但是,完全模拟这个思路不太好写,technique1和technique2的差经过排序之后 和technique1、technique2对应关系都乱了,很难一一对应,并排除使用已经干过的任务,于是我们把思路做简化
所以我们这样写:
先把所有的technique2加起来,存进 a n s ans ans,并统计所有的technique1[i]-technique2[i],装进一个数组 d i f f diff diff,这里的 d i f f [ i ] diff[i] diff[i]如果非得解释出一个意思的话,就是:使用技巧1之后对最终答案的影响,是正数就是往好的影响,是负数就是往坏的影响(看到后面你就懂了)
对 d i f f diff diff进行降序排序
这时候先让 a n s ans ans累加上正的 d i f f diff diff(因为所有的technique2加起来不一定就是最优的答案(这你们看到这里也都知道)而正的 d i f f diff diff就是那些选技巧1更优的任务比选技巧2多得的分值,加上它们也就是说把这些亏的分值补上(对最终的答案做好的影响),这些任务也就最优了,当然如果加的超过了k 也不用担心,毕竟是好的影响嘛,多出去更好)
如果现在累加上的 d i f f diff diff还还不够 k k k个的话,就继续累加到第 k k k个 d i f f diff diff元素(因为,为了满足题目中的要求,我们必须使用k次技巧1,而 d i f f [ i ] diff[i] diff[i]代表的就是使用技巧1之后对最终答案的影响,也就是说,加到k就是把技巧1 使用到K次,把 d i f f diff diff加到k就是把使用技巧1之后对最终答案的影响更新(不管是好的影响还是坏的影响),而对 d i f f diff diff进行降序排序就是为了让好的影响更多更大,尽量使用前k个的技巧1时不会出现坏的影响,即使出现也是更小的!只加到k个是因为,进到这种情况的,都是好的影响用完了,现在每多加一次都是对答案的进一步的坏影响,所以只加到k就好)
可能有点难懂,多读几遍,并结合下面的代码理解
根据这个简化的思路,实现一下代码:
cpp
class Solution {
private:
static bool cmp(int x, int y) {
return x > y;
}
public:
long long maxPoints(vector<int>& technique1, vector<int>& technique2, int k) {
int n = technique1.size();
vector<int> diff(n);
long long ans = 0;
for (int i = 0; i < n; ++i) {
diff[i] = technique1[i] - technique2[i];
ans += technique2[i];
}
sort(diff.begin(), diff.end(), cmp);
int posCount = 0;
for (int i = 0; i < n && diff[i] > 0; ++i) {
ans += diff[i];
++posCount;
}
if (posCount < k) {
for (int i = posCount; i < k; ++i) {
ans += diff[i];
}
}
return ans;
}
};
由于这个题主要讲解思路算法,而且代码中没有出现罕见语法和一些巧妙的写法,所以不对代码进行逐句解析

最后我们通过了
总结
这个题目的思路推理我完全写进了思路分析,感谢你查看本篇题解,谢谢!