问题描述
小C来到了一家饭馆,这里共有 nn 道菜,第 ii 道菜的价格为 a_i。其中一些菜中含有蘑菇,s_i 代表第 ii 道菜是否含有蘑菇。如果 s_i = '1',那么第 ii 道菜含有蘑菇,否则没有。
小C希望点 kk 道菜,且希望总价格尽可能低。由于她不喜欢蘑菇,她希望所点的菜中最多只有 mm 道菜含有蘑菇。小C想知道在满足条件的情况下能选出的最小总价格是多少。如果无法按照要求选择菜品,则输出-1。
题目分析
给定了一些菜品的价格和是否包含蘑菇的信息,要求我们在限定条件下选择 k 道菜,其中最多只能有 m 道菜包含蘑菇,并且要使得所选菜品的总价格尽可能低。若无法满足要求,则返回 -1。
解决思路
-
分类菜品:我们可以将菜品分为两类:含有蘑菇的菜品和不含蘑菇的菜品。
-
排序选择:为了让总价格尽可能低,我们应当选择价格最便宜的菜品。所以我们可以分别对含蘑菇和不含蘑菇的菜品按价格进行排序,从价格最便宜的开始选择。
-
选择策略:
- 最多选择
m道含蘑菇的菜,剩下的从不含蘑菇的菜中选择。 - 如果所选菜品数不足
k,则需要从含蘑菇的菜中补充,直到总共选择了k道菜。
- 最多选择
-
边界检查:在选择的过程中,如果没有足够的菜品可以选择,或者选出的菜品不满足限制条件(如蘑菇菜品超过
m道),则返回-1。
详细步骤
-
分类并排序:
- 将所有菜品分为两组:含蘑菇和不含蘑菇,分别按照价格升序排序。
-
计算选择的最小价格:
- 先选择尽量多的便宜的不含蘑菇的菜品,如果不够,则选择含蘑菇的菜品,确保总共选择的菜品数为
k,且含蘑菇的菜品数不超过m。
- 先选择尽量多的便宜的不含蘑菇的菜品,如果不够,则选择含蘑菇的菜品,确保总共选择的菜品数为
-
返回结果:
- 如果能选择足够的菜品并且符合蘑菇数量限制,则返回最小总价格,否则返回
-1。
- 如果能选择足够的菜品并且符合蘑菇数量限制,则返回最小总价格,否则返回
代码
def solution(s: str, a: list, m: int, k: int) -> int:
mushroom_dishes = []
non_mushroom_dishes = []
for i in range(len(s)):
if s[i] == '1': # 有蘑菇
mushroom_dishes.append(a[i])
else: # 无蘑菇
non_mushroom_dishes.append(a[i])
# 对两类菜品按价格升序排序
mushroom_dishes.sort()
non_mushroom_dishes.sort()
# 检查是否有足够的菜品
if len(mushroom_dishes) + len(non_mushroom_dishes) < k:
return -1
# 尝试选择不超过 m 道含蘑菇的菜品,剩下从不含蘑菇的菜品中选择
min_total_price = float('inf')
# 尝试从 0 到 m 道含蘑菇的菜品
for mushroom_count in range(max(0, k - len(non_mushroom_dishes)), min(m, len(mushroom_dishes)) + 1):
non_mushroom_count = k - mushroom_count
if non_mushroom_count <= len(non_mushroom_dishes):
# 选择最便宜的mushroom_count道含蘑菇的菜品和最便宜的non_mushroom_count道非蘑菇菜品
total_price = sum(mushroom_dishes[:mushroom_count]) + sum(non_mushroom_dishes[:non_mushroom_count])
min_total_price = min(min_total_price, total_price)
# 如果未找到合法的选择方案,则返回 -1
return min_total_price if min_total_price != float('inf') else -1
if __name__ == '__main__':
print(solution("001", [10, 20, 30], 1, 2) == 30)
print(solution("111", [10, 20, 30], 1, 2) == -1)
print(solution("0101", [5, 15, 10, 20], 2, 3) == 30)
复杂度分析
-
时间复杂度:
- 分两类菜品:O(n)。
- 对两类菜品分别排序:O(n log n)。
- 遍历
0到m的不同蘑菇菜品数量并计算总价格:最多 O(m),但因为最大 m 为 n,实际上为 O(n)。
因此,总时间复杂度为 O(n log n)。
-
空间复杂度:
- 我们需要存储两个列表(蘑菇和非蘑菇菜品),空间复杂度是 O(n)。
为什么会想到这个方法
我之所以选择这种方法,主要是基于以下几个方面的思考:
-
分类选择问题:题目给定了菜品是否含有蘑菇的属性,这使得我们可以把问题分成两类:含蘑菇和不含蘑菇的菜品。这样可以简化问题,因为我们只需要对两类菜品分别进行处理。
-
贪心策略:
- 总价格尽可能低:为了让总价格尽量低,我们应该优先选择价格便宜的菜品。这是一个典型的贪心选择策略,即每次都选择当前最便宜的菜品,直至满足选择数量为止。
- 因此,我们对两类菜品分别排序,选择最便宜的菜品,确保价格最小化。
-
蘑菇限制的考虑:
- 我们的目标是选择 最多 m 道蘑菇菜品,但也必须选择 k 道菜。由于蘑菇菜品的数量是有限的,因此我们要控制在选择菜品的过程中,蘑菇菜品的数量不超过
m。 - 这样,我们就需要尝试选择不同数量的蘑菇菜品(从 0 到
m道蘑菇菜品),然后用不含蘑菇的菜品来补足剩下的部分。
- 我们的目标是选择 最多 m 道蘑菇菜品,但也必须选择 k 道菜。由于蘑菇菜品的数量是有限的,因此我们要控制在选择菜品的过程中,蘑菇菜品的数量不超过
-
遍历不同蘑菇菜品数:
- 我们需要确保所选的菜品中蘑菇菜品数不超过
m,同时总共选k道菜。为了实现这一点,我们考虑遍历可能的蘑菇菜品数量,从 0 道到最多m道蘑菇菜品,计算每种情况下的总价格,并取最小值。
通过这种遍历,我们能保证不会漏掉任何可能的组合,同时确保每次选择的都是最便宜的菜品。
- 我们需要确保所选的菜品中蘑菇菜品数不超过