问题描述
小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[i] = '1')和不含蘑菇的菜(s[i] = '0')。 - 分别对这两类菜进行价格排序,以便选出最便宜的菜。
- 把菜分为两类:含有蘑菇的菜(
-
选择策略:
-
如果小C需要选的菜总数为 k,且最多有 m 道菜可以含有蘑菇,那么可以尝试不同的组合:
- 选 x 道不含蘑菇的菜,其中 x 从 max(0,k−m) 到 min(k,无蘑菇菜的总数)。
- 剩余的 k−x道菜必须从含有蘑菇的菜中选择。
-
对于每种组合,计算总价格,选择满足条件的最小价格。
-
-
无法满足条件的情况:
- 如果不可能选出 k 道菜,输出 -1。
实现步骤
- 分离无蘑菇菜和有蘑菇菜,分别存储其价格。
- 对两类菜的价格进行升序排序。
- 使用双指针或者前缀和的方法,快速计算选定数量的菜品的最低价格。
- 遍历可能的 x 值(无蘑菇菜的数量),计算总价格,记录最小值。
- 返回结果。
以下是代码实现:
def min_price(s, a, m, k):
# 将含有蘑菇和无蘑菇的菜分开
no_mushroom = []
with_mushroom = []
for i in range(len(s)):
if s[i] == '0':
no_mushroom.append(a[i])
else:
with_mushroom.append(a[i])
# 对两类菜进行排序
no_mushroom.sort()
with_mushroom.sort()
# 如果总菜数不够 k 道菜,直接返回 -1
if len(no_mushroom) + len(with_mushroom) < k:
return -1
# 计算前缀和,方便快速求和
prefix_no_mushroom = [0] * (len(no_mushroom) + 1)
prefix_with_mushroom = [0] * (len(with_mushroom) + 1)
for i in range(1, len(no_mushroom) + 1):
prefix_no_mushroom[i] = prefix_no_mushroom[i - 1] + no_mushroom[i - 1]
for i in range(1, len(with_mushroom) + 1):
prefix_with_mushroom[i] = prefix_with_mushroom[i - 1] + with_mushroom[i - 1]
# 尝试不同的 x 值
min_cost = float('inf')
for x in range(max(0, k - m), min(k, len(no_mushroom)) + 1):
y = k - x # 含有蘑菇的菜的数量
if y <= len(with_mushroom):
cost = prefix_no_mushroom[x] + prefix_with_mushroom[y]
min_cost = min(min_cost, cost)
return min_cost if min_cost != float('inf') else -1
# 测试样例
print(min_price("001", [10, 20, 30], 1, 2)) # 输出:30
print(min_price("111", [10, 20, 30], 1, 2)) # 输出:-1
print(min_price("0101", [5, 15, 10, 20], 2, 3)) # 输出:30
代码说明
-
分类和排序:
- 把所有菜按是否含蘑菇分成两类,然后分别排序,以便优先选出最便宜的菜。
-
前缀和:
- 用前缀和快速计算任意数量的菜品的总价格。
-
组合尝试:
- 遍历 xx 值,表示选取的无蘑菇菜的数量,动态计算总价格,并更新最小值。
-
边界处理:
- 如果总菜数不足 kk,直接返回 -1。
- 如果某种组合无法满足条件,也不会更新最小值。
复杂度分析
-
时间复杂度:
- 排序:O(nlogn)。
- 遍历组合:O(k)。
- 总复杂度:O(nlogn)。
-
空间复杂度:
- 需要额外的前缀和数组,空间复杂度为 O(n)。