36 饭馆菜品选择问题刷题笔记 | 豆包MarsCode AI刷题

66 阅读4分钟

36 饭馆菜品选择问题刷题笔记

问题分析

题目要求:

  • 给出一组菜品,每个菜品有价格和是否含蘑菇的标识。
  • 小C需要点出 总共 k 道菜
  • 最多 m 道菜含有蘑菇
  • 最终目标是 满足条件的菜品组合的总价格最低
  • 如果无法满足条件,则返回 -1

思路分析

  1. 分类处理

    • 根据是否含有蘑菇,将菜品分为两类:

      • 含蘑菇菜集合 mushroom_dishes
      • 不含蘑菇菜集合 non_mushroom_dishes
  2. 排序优化

    • 对每类菜按照价格从低到高排序。
    • 这样可以保证在选菜时优先选择价格最低的菜,满足题目“价格尽可能低”的要求。
  3. 尝试所有可能的组合

    • 遍历可能的蘑菇菜数量,从 0min(m, k)

    • 剩余的菜数量由不含蘑菇菜补充:non_mushrooms_count = k - mushrooms_count

    • 检查:

      • 蘑菇菜是否足够。
      • 不含蘑菇菜是否足够。
  4. 计算总价格并记录最优解

    • 对每种组合,计算总价格。
    • 保留满足条件的组合中最低的总价格。
  5. 处理特殊情况

    • 如果遍历后找不到符合条件的组合,返回 -1

图解

  1. 分类示意: 假设有以下数据:

    s = "0101"
    a = [5, 15, 10, 20]
    

    分为:

    含蘑菇菜:mushroom_dishes = [15, 20]
    不含蘑菇菜:non_mushroom_dishes = [5, 10]
    
  2. 遍历过程

    • 如果 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

代码解析

  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()
    
  2. 遍历可能的组合

    • 通过 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)
    
  3. 结果处理

    • 如果找到满足条件的组合,返回 min_cost
    • 如果遍历后 min_cost 仍为正无穷,说明没有满足条件的组合,返回 -1
    return min_cost if min_cost != float('inf') else -1
    

时间复杂度分析

  1. 分类和排序:O(n log n),其中 n 是菜品数量。
  2. 遍历组合:O(k),其中 k 是需要点的菜品数量。
  3. 总复杂度: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

总结

  • 分类、排序是解决此类问题的常用技巧。
  • 遍历组合是一种穷举优化的方式,通过限制最大值减少复杂度。
  • 稳妥处理特殊情况(如无解)是关键点。