问题描述
小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
解题:
参数:
- s (str): 长度为n的字符串,s_i代表第i道菜是否含有蘑菇('1'表示含有,'0'表示不含有)。
- a (list of int): 长度为n的整数列表,a_i代表第i道菜的价格。
- m (int): 所点的菜中最多可以含有m道蘑菇。
- k (int): 需要点的菜的数量。
返回:
- int: 满足条件的最小总价格。如果无法满足条件,则返回-1。
"""
# 分离菜品为不含蘑菇(Group A)和含蘑菇(Group B)的两组
group_A = [price for si, price in zip(s, a) if si == '0']
group_B = [price for si, price in zip(s, a) if si == '1']
# 如果k大于总菜品数,无法选择
if k > len(a):
return -1
# 排序两组菜品,以便选择最便宜的
group_A_sorted = sorted(group_A)
group_B_sorted = sorted(group_B)
# 计算两组的前缀和,便于快速计算任意数量的最小总价格
prefix_A = [0]
for price in group_A_sorted:
prefix_A.append(prefix_A[-1] + price)
prefix_B = [0]
for price in group_B_sorted:
prefix_B.append(prefix_B[-1] + price)
# 初始化最小总价格为无穷大
min_total = float('inf')
# 遍历可能的含蘑菇菜品数量,从0到min(m, len(B), k))
max_b_count = min(m, len(group_B_sorted), k)
for b_count in range(0, max_b_count + 1):
a_count = k - b_count # 不含蘑菇的菜品数量
# 检查是否有足够的不含蘑菇菜品
if a_count <= len(group_A_sorted):
total_price = prefix_A[a_count] + prefix_B[b_count]
if total_price < min_total:
min_total = total_price
# 如果找到有效的组合,返回最小总价格;否则,返回-1
return min_total if min_total != float('inf') else -1
- **分组菜品**:
- **Group A**(不含蘑菇的菜品):`s_i = '0'`
- **Group B**(含蘑菇的菜品):`s_i = '1'`
这样,我们可以分别处理不含蘑菇和含蘑菇的菜品,便于后续选择最便宜的组合。
-
排序:
- 对Group A和Group B中的菜品按价格从低到高进行排序。这样,我们可以轻松选择最便宜的菜品。
-
前缀和计算:
- 计算Group A和Group B的前缀和。前缀和
prefix_A[i]表示Group A中前i道菜的总价格,prefix_B[j]表示Group B中前j道菜的总价格。 - 这样,在选择特定数量的菜品时,可以快速计算总价格。
- 计算Group A和Group B的前缀和。前缀和
-
枚举含蘑菇菜品的数量:
- 遍历
b_count从0到min(m, len(Group B), k),表示选择的含蘑菇菜品数量。 - 对于每个
b_count,计算需要从Group A中选择的菜品数量a_count = k - b_count。 - 检查是否有足够的Group A菜品供选择。如果有,计算总价格
total_price = prefix_A[a_count] + prefix_B[b_count],并更新最小总价格min_total。
- 遍历
-
结果判断:
- 如果找到满足条件的组合,返回最小总价格。
- 如果没有满足条件的组合(例如,无法选择足够的不含蘑菇的菜品),返回
-1。
复杂度分析:
-
时间复杂度:
- 分组和排序需要O(n log n)的时间,其中n是菜品数量。
- 枚举
b_count的范围为O(k)(实际为min(m, len(Group B), k)+1)。 - 前缀和的计算和总价的比较均为O(n)。
- 因此,总时间复杂度为O(n log n)。
-
空间复杂度:
- 需要额外的空间来存储Group A和Group B,以及它们的前缀和,空间复杂度为O(n)。
注意事项:
-
边界情况:
- 当k = 0时,理论上总价为0,但根据题意,小C需要点k道菜,因此k >=1。
- 当m = 0时,所有选中的菜品必须不含蘑菇。
- 当m >= k时,可以选择所有k道菜都含蘑菇(如果需要)。
- 当Group A或Group B不足以满足选择需求时,需返回-1。
-
输入有效性:
- 确保字符串
s和列表a的长度相同,即n = len(s) = len(a)。 - 处理可能的重复菜品价格,虽然不影响总价格的计算。
- 确保字符串
-
优化:
- 使用前缀和可以优化多次求和的操作,避免重复计算。