日常刷题-饭馆菜品选择问题 | 豆包MarsCode AI刷题

97 阅读4分钟

问题描述

小C来到了一家饭馆,这里共有 nn 道菜,第 ii 道菜的价格为 a_i。其中一些菜中含有蘑菇,s_i 代表第 ii 道菜是否含有蘑菇。如果 s_i = '1',那么第 ii 道菜含有蘑菇,否则没有。

小C希望点 kk 道菜,且希望总价格尽可能低。由于她不喜欢蘑菇,她希望所点的菜中最多只有 mm 道菜含有蘑菇。小C想知道在满足条件的情况下能选出的最小总价格是多少。如果无法按照要求选择菜品,则输出-1


测试样例

样例1:

输入:s = "001", a = [10, 20, 30], m = 1, k = 2
输出:30

样例2:

输入:s = "111", a = [10, 20, 30], m = 1, k = 2
输出:-1

样例3:

输入:s = "0101", a = [5, 15, 10, 20], m = 2, k = 3
输出:30

Solution

过程

  • 组合选择:可以从n道菜中选择k道菜。对所有可能的选择进行枚举,以找到符合条件的组合。
  • 检查蘑菇限制:对于每一个可能的选择组合,检查其中含有蘑菇的菜的数量是否不超过m
  • 计算总价格:计算每个符合条件的组合的总价格。
  • 最小价格:在所有符合条件的组合中,找出总价格最小的一个。
  • 如果没有任何符合条件的组合,则返回-1。

示例代码

from itertools import combinations

def min_total_price(s, a, m, k):
    n = len(a)
    min_price = float('inf')
    found = False
    
    for combo in combinations(range(n), k):
        mushroom_count = sum(1 for i in combo if s[i] == '1')
        if mushroom_count <= m:
            found = True
            total_price = sum(a[i] for i in combo)
            if total_price < min_price:
                min_price = total_price
    
    return min_price if found else -1

提交看看,OK完美AC~

复杂度分析

  • 时间复杂度:O( C(n, k) * k ),其中C(n, k)是从n个菜中选择k个菜的组合数。对于每一个组合,我们需要计算总价格和蘑菇数量,这需要O(k)的时间。
  • 空间复杂度:O(k)用于临时存储组合中的菜品索引。

这种解法虽然直接,但效率较低,适用于数据规模较小的情况。

Solution 2

  • 分类存储菜品:首先将所有菜品根据是否含有蘑菇分成两类,以便分别处理。
  • 排序:对两类菜品的价格分别排序,以便能够优先选择价格最低的菜品。
  • 前缀和计算:计算两类菜品的前缀和数组,这样可以快速得到从最便宜的开始选择x道菜的总价格。
  • 枚举可能的选择:枚举选择的蘑菇菜的数量,从0到m,并计算对应的非蘑菇菜的数量,判断是否合法(即是否在可选范围内)。

过程

  1. 分类存储

    • 遍历as,将含有蘑菇的菜价格加入mushroom列表,不含蘑菇的菜价格加入non_mushroom列表。
  2. 排序

    • 分别对mushroomnon_mushroom列表进行升序排序。
  3. 计算前缀和

    • 使用itertools.accumulate计算出前缀和数组prefix_mushroomprefix_non_mushroom,第一个元素为0便于计算(表示不选取任何菜时的价格)。
  4. 枚举选择

    • 枚举蘑菇菜的数量t从0到min(m, num_mushroom)
    • 计算需要的非蘑菇菜数量needed_non_mushroom = k - t
    • 如果needed_non_mushroom在合法范围内(即非负且不超过num_non_mushroom),计算总价格。
  5. 更新最小价格

    • 使用前缀和数组快速计算出当前选择下的总价格total_price = prefix_mushroom[t] + prefix_non_mushroom[needed_non_mushroom]
    • 更新min_total为所有可能选择中的最小值。

Code

def solution(s: str, a: list, m: int, k: int) -> int:
    # 将含蘑菇和不含蘑菇的菜分别存放
    mushroom = []
    non_mushroom = []
    
    for si, ai in zip(s, a):
        if si == '1':
            mushroom.append(ai)
        else:
            non_mushroom.append(ai)
    
    # 对价格进行排序,便于选取最便宜的菜
    mushroom.sort()
    non_mushroom.sort()
    
    # 前缀和,用于快速计算某一数量的菜的总价格
    from itertools import accumulate
    prefix_mushroom = [0] + list(accumulate(mushroom))
    prefix_non_mushroom = [0] + list(accumulate(non_mushroom))
    
    num_mushroom = len(mushroom)
    num_non_mushroom = len(non_mushroom)
    
    min_total = float('inf')
    
    # 枚举选取的蘑菇菜的数量,从0到min(m, num_mushroom)
    for t in range(0, min(m, num_mushroom)+1):
        needed_non_mushroom = k - t
        if needed_non_mushroom < 0:
            continue
        if needed_non_mushroom > num_non_mushroom:
            continue
        total_price = prefix_mushroom[t] + prefix_non_mushroom[needed_non_mushroom]
        if total_price < min_total:
            min_total = total_price
    
    return min_total if min_total != float('inf') else -1

复杂度分析

  • 时间复杂度:O(n log n),主要来自对mushroomnon_mushroom进行排序。计算前缀和及枚举的时间复杂度均为O(n)。
  • 空间复杂度:O(n),用于存储分类后的菜品列表和前缀和。

这个解法通过分类和前缀和技巧,优化了暴力枚举的时间复杂度,使其更高效地解决了问题。