问题描述
小C来到了一家饭馆,这里共有 道菜,第 ii 道菜的价格为 a_i。其中一些菜中含有蘑菇,s_i 代表第 道菜是否含有蘑菇。如果 s_i = '1',那么第 道菜含有蘑菇,否则没有。
小C希望点 道菜,且希望总价格尽可能低。由于她不喜欢蘑菇,她希望所点的菜中最多只有 道菜含有蘑菇。小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,并计算对应的非蘑菇菜的数量,判断是否合法(即是否在可选范围内)。
过程
-
分类存储:
- 遍历
a和s,将含有蘑菇的菜价格加入mushroom列表,不含蘑菇的菜价格加入non_mushroom列表。
- 遍历
-
排序:
- 分别对
mushroom和non_mushroom列表进行升序排序。
- 分别对
-
计算前缀和:
- 使用
itertools.accumulate计算出前缀和数组prefix_mushroom和prefix_non_mushroom,第一个元素为0便于计算(表示不选取任何菜时的价格)。
- 使用
-
枚举选择:
- 枚举蘑菇菜的数量
t从0到min(m, num_mushroom)。 - 计算需要的非蘑菇菜数量
needed_non_mushroom = k - t。 - 如果
needed_non_mushroom在合法范围内(即非负且不超过num_non_mushroom),计算总价格。
- 枚举蘑菇菜的数量
-
更新最小价格:
- 使用前缀和数组快速计算出当前选择下的总价格
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),主要来自对
mushroom和non_mushroom进行排序。计算前缀和及枚举的时间复杂度均为O(n)。 - 空间复杂度:O(n),用于存储分类后的菜品列表和前缀和。
这个解法通过分类和前缀和技巧,优化了暴力枚举的时间复杂度,使其更高效地解决了问题。