1402.做菜顺序

【LetMeFly】1402.做菜顺序:排序 + 前缀(贪心) - 按思路讲解

力扣题目链接:https://leetcode.cn/problems/reducing-dishes/

一个厨师收集了他 n 道菜的满意程度 satisfaction ,这个厨师做出每道菜的时间都是 1 单位时间。

一道菜的 「喜爱时间」系数定义为烹饪这道菜以及之前每道菜所花费的时间乘以这道菜的满意程度,也就是 time[i]*satisfaction[i] 。

请你返回做完所有菜 「喜爱时间」总和的最大值为多少。

你可以按 任意 顺序安排做菜的顺序,你也可以选择放弃做某些菜来获得更大的总和。

 

示例 1:

输入:satisfaction = [-1,-8,0,5,-9]
输出:14
解释:去掉第二道和最后一道菜,最大的喜爱时间系数和为 (-1*1 + 0*2 + 5*3 = 14) 。每道菜都需要花费 1 单位时间完成。

示例 2:

输入:satisfaction = [4,3,2]
输出:20
解释:按照原来顺序相反的时间做菜 (2*1 + 3*2 + 4*3 = 20)

示例 3:

输入:satisfaction = [-1,-4,-5]
输出:0
解释:大家都不喜欢这些菜,所以不做任何菜可以获得最大的喜爱时间系数。

 

提示:

  • n == satisfaction.length
  • 1 <= n <= 500
  • -1000 <= satisfaction[i] <= 1000

方法一:排序 + 前缀(贪心)

假设选了一个做菜序列$[a,b,c,d,\cdots]$,那么很容易想到这道题的关键:菜开始的越晚权重越大。因此本题的核心思路(贪心)是:satisfaction越高的菜越往后放

二话不说,先排个序吧。反正satisfaction为正的菜是一定要做的(只有好处没有坏处),所以就二分查找一下排序后第一个satisfaction为正的菜的位置positiveLocation,从positiveLocation到数组末尾的“正菜”先依次加入到做菜序列中。

好了,“正菜”处理完了,那么“负菜”就一无是处了吗?当然不是。每往做菜序列前面增加一道菜品,后面每道菜的权重都会加一。也就是说,在现在的做菜序列基础上,每新加一道负菜,总分的变化是“正菜satisfaction之和 加上 负菜satisfaction之和”(含新增的负菜。

那么到底怎么处理负菜呢?只需要统计一下“正菜satisfaction之和sumPositive”,以及加上这道负菜的话“负菜satisfaction之和sumNegative”,如果$abs(sumPositive) >= abs(sumNegative)$,那么就将这道菜加入到做菜序列之首,否则就不加。

对于同为负数的两道菜,当然是优先选择较大的那个负数菜喽(能选$-1$不选$-2$)。

  • 时间复杂度$O(n\times \log n)$。排序的时间复杂度是$n\log n$,二分(只二分了一次)的时间复杂度为$\log n$,选菜的总时间复杂度不超过$O(n)$。其中$n=len(satisfaction)$。
  • 空间复杂度$O(\log n)$。排序的空间复杂度为$O(\log n)$,其余部分空间复杂度都为$O(1)$。

其实本题中也可以不使用二分查找来获取“第一个正菜”的位置,可以使用遍历(复杂的不变)或从数组末尾开始往前遍历(不一定从最小的正菜开始)。

这里感谢github@5hepp以及github@HuangSizhe的建议~

AC代码

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
int maxSatisfaction(vector<int>& satisfaction) {
sort(satisfaction.begin(), satisfaction.end());
int positiveLocation = upper_bound(satisfaction.begin(), satisfaction.end(), 0) - satisfaction.begin();
int ans = 0, sumPositive = 0;
for (int i = positiveLocation; i < satisfaction.size(); i++) {
ans += (i - positiveLocation + 1) * satisfaction[i];
sumPositive += satisfaction[i];
}
int sumNegative = 0;
for (int i = positiveLocation - 1; i >= 0; i--) {
sumNegative += satisfaction[i];
if (-sumNegative > sumPositive) {
break;
}
ans += sumPositive + sumNegative;
}
return ans;
}
};

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# from typing import List
# from bisect import bisect_right


class Solution:
def maxSatisfaction(self, satisfaction: List[int]) -> int:
satisfaction.sort()
positiveLocation = bisect_right(satisfaction, 0)
ans, sumPositive = 0, 0
for i in range(positiveLocation, len(satisfaction)):
ans += (i - positiveLocation + 1) * satisfaction[i]
sumPositive += satisfaction[i]
sumNegative = 0
for i in range(positiveLocation - 1, -1, -1):
sumNegative += satisfaction[i]
if -sumNegative > sumPositive:
break
ans += sumPositive + sumNegative
return ans

同步发文于CSDN,原创不易,转载经作者同意后请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/133974648


1402.做菜顺序
https://blog.letmefly.xyz/2023/10/22/LeetCode 1402.做菜顺序/
作者
Tisfy
发布于
2023年10月22日
许可协议