36 饭馆菜品选择问题刷题笔记
问题分析
题目要求:
- 给出一组菜品,每个菜品有价格和是否含蘑菇的标识。
- 小C需要点出 总共
k道菜。 - 最多
m道菜含有蘑菇。 - 最终目标是 满足条件的菜品组合的总价格最低。
- 如果无法满足条件,则返回
-1。
思路分析
-
分类处理:
-
根据是否含有蘑菇,将菜品分为两类:
- 含蘑菇菜集合
mushroom_dishes。 - 不含蘑菇菜集合
non_mushroom_dishes。
- 含蘑菇菜集合
-
-
排序优化:
- 对每类菜按照价格从低到高排序。
- 这样可以保证在选菜时优先选择价格最低的菜,满足题目“价格尽可能低”的要求。
-
尝试所有可能的组合:
-
遍历可能的蘑菇菜数量,从
0到min(m, k)。 -
剩余的菜数量由不含蘑菇菜补充:
non_mushrooms_count = k - mushrooms_count。 -
检查:
- 蘑菇菜是否足够。
- 不含蘑菇菜是否足够。
-
-
计算总价格并记录最优解:
- 对每种组合,计算总价格。
- 保留满足条件的组合中最低的总价格。
-
处理特殊情况:
- 如果遍历后找不到符合条件的组合,返回
-1。
- 如果遍历后找不到符合条件的组合,返回
图解
-
分类示意: 假设有以下数据:
s = "0101" a = [5, 15, 10, 20]分为:
含蘑菇菜:mushroom_dishes = [15, 20] 不含蘑菇菜:non_mushroom_dishes = [5, 10] -
遍历过程:
-
如果
k = 3, m = 2,可以有以下可能组合:- 选 0 道蘑菇菜 + 3 道不含蘑菇菜(无解,因为不含蘑菇菜只有 2 道)。
- 选 1 道蘑菇菜 + 2 道不含蘑菇菜。
- 选 2 道蘑菇菜 + 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()
# 初始化最小总价格为正无穷
min_cost = float('inf')
# 遍历可能的蘑菇菜数量
for mushrooms_count in range(min(m, k) + 1): # 从 0 到 min(m, k)
non_mushrooms_count = k - mushrooms_count # 剩余需要的不含蘑菇菜数量
# 检查是否有足够的蘑菇菜和不含蘑菇菜
if mushrooms_count <= len(mushroom_dishes) and non_mushrooms_count <= len(non_mushroom_dishes):
# 计算当前组合的总价格
cost = sum(mushroom_dishes[:mushrooms_count]) + sum(non_mushroom_dishes[:non_mushrooms_count])
# 更新最小总价格
min_cost = min(min_cost, cost)
# 如果未找到满足条件的组合,返回 -1
return min_cost if min_cost != float('inf') else -1
代码解析
-
分类与排序:
- 代码利用
for循环和条件判断,区分含蘑菇与不含蘑菇的菜品。 - 对每类菜品进行排序,使得价格最低的菜品优先被选择。
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() - 代码利用
-
遍历可能的组合:
- 通过
for循环,枚举不同的蘑菇菜数量mushrooms_count。 - 每次遍历中,计算剩余需要的不含蘑菇菜数量
non_mushrooms_count。 - 检查这两类菜是否足够。
- 如果足够,计算总价格,并与当前最优价格比较。
for mushrooms_count in range(min(m, k) + 1): non_mushrooms_count = k - mushrooms_count # 剩余需要的不含蘑菇菜数量 if mushrooms_count <= len(mushroom_dishes) and non_mushrooms_count <= len(non_mushroom_dishes): cost = sum(mushroom_dishes[:mushrooms_count]) + sum(non_mushroom_dishes[:non_mushrooms_count]) min_cost = min(min_cost, cost) - 通过
-
结果处理:
- 如果找到满足条件的组合,返回
min_cost。 - 如果遍历后
min_cost仍为正无穷,说明没有满足条件的组合,返回-1。
return min_cost if min_cost != float('inf') else -1 - 如果找到满足条件的组合,返回
时间复杂度分析
- 分类和排序:
O(n log n),其中n是菜品数量。 - 遍历组合:
O(k),其中k是需要点的菜品数量。 - 总复杂度:
O(n log n)。
测试与结果
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
-
样例1:
- 不含蘑菇菜:[10]
- 含蘑菇菜:[20, 30]
- 选 1 道含蘑菇 + 1 道不含蘑菇,最低总价格:
30。
-
样例2:
- 全部是含蘑菇菜,无法满足条件,输出
-1。
- 全部是含蘑菇菜,无法满足条件,输出
-
样例3:
- 不含蘑菇菜:[5, 10]
- 含蘑菇菜:[15, 20]
- 选 2 道含蘑菇 + 1 道不含蘑菇,最低总价格:
30。
总结
- 分类、排序是解决此类问题的常用技巧。
- 遍历组合是一种穷举优化的方式,通过限制最大值减少复杂度。
- 稳妥处理特殊情况(如无解)是关键点。