一、问题背景
小 C 来到一家饭馆,饭馆里共有 n 道菜可供选择。每道菜都有自己的价格,用数组 a 表示,其中第 i 道菜的价格为 a_i 。除此之外,每道菜还有个特殊属性,就是是否含有蘑菇,用字符串 s 来表示,如果 s_i = '1',意味着第 i 道菜含有蘑菇,要是 s_i = '0',则这道菜不含蘑菇。
小 C 有自己的点菜要求,她希望点 k 道菜,并且想让总价格尽可能低。同时,由于她不太喜欢蘑菇,所以要求所点的菜中最多只能有 m 道菜含有蘑菇。现在的任务就是要找出在满足这些条件的情况下,能选出的最小总价格是多少,如果没办法按照要求选择菜品,那就输出 -1。
二、示例分析
为了更清晰地理解这个问题,咱们来看几个具体的测试样例:
样例 1
- 输入:
s = "001", a = [10, 20, 30], m = 1, k = 2 - 输出:
30
解释:这里有三道菜,价格分别是10、20、30,只有第三道菜含有蘑菇。小 C 要选2道菜且最多允许1道有蘑菇的菜,那么选择价格为10和20的菜不符合要求(因为没选含蘑菇的那道,数量不够k = 2道),而选择价格为20和30的菜就满足条件了,总价格为30,所以输出30。
样例 2
- 输入:
s = "111", a = [10, 20, 30], m = 1, k = 2 - 输出:
-1
解释:三道菜品都含有蘑菇,而小 C 最多只能选1道含蘑菇的菜且要选2道菜,按照这个要求是没办法完成点菜的,所以输出-1。
样例 3
- 输入:
s = "0101", a = [5, 15, 10, 20], m = 2, k = 3 - 输出:
30
解释:在这四道菜中进行选择,要选3道菜且最多允许2道含蘑菇的菜。可以选择价格为5、10、15的菜(其中有1道含蘑菇的菜)或者选择价格为5、10、20的菜(其中有1道含蘑菇的菜)等组合,经过比较发现选择价格为5、10、15的菜总价格最低,为30,所以输出30。
三、Python 题解思路
要解决这个问题,我们可以采用排序结合贪心算法的思路,具体步骤如下:
-
将菜品信息整合:
首先,我们可以把每道菜的价格和是否含蘑菇这两个信息整合到一起,比如可以使用一个包含元组的列表,每个元组中第一个元素是价格,第二个元素是是否含蘑菇的标识(0或1),方便后续的操作和比较。 -
对菜品进行排序:
按照价格对所有菜品进行升序排序。这样做的好处是,我们在选择菜品的时候,优先选择价格低的,更有可能得到最小总价格。 -
枚举含蘑菇菜品的数量:
从0到m枚举含蘑菇菜品的数量t(t表示我们选择含蘑菇菜品的个数),对于每一个t值:- 先从不含蘑菇的菜品中选
k - t个(如果数量足够的话),这些都是价格相对较低的不含蘑菇的菜品。 - 再从含蘑菇的菜品中选
t个(同样需要数量足够),选择价格相对较低的含蘑菇菜品。 - 计算这样选出的
k道菜的总价格,记录下所有可能情况中的最小总价格。
- 先从不含蘑菇的菜品中选
-
判断是否有可行解:
如果在上述所有枚举情况中,都没办法选出k道菜(比如含蘑菇的菜品数量不够,或者不含蘑菇的菜品数量不够等情况),那就说明没办法按照要求点菜,按照题目要求输出-1;否则,输出记录下来的最小总价格。
以下是 Python 代码实现示例:
def solution(s: str, a: list, m: int, k: int) -> int:
dishes = list(zip(a, map(int, s)))
# 按照价格对菜品进行升序排序,key参数指定了按照元组中的第一个元素(即价格)来排序
dishes.sort(key=lambda x: x[0])
min_price = float('inf')
# 枚举含蘑菇菜品的数量t,范围从0到m和k中较小的值 + 1(因为要包含m和k本身的情况)
for t in range(min(m + 1, k + 1)):
# 筛选出不含蘑菇的菜品价格列表
non_mushroom = [dish[0] for dish in dishes if dish[1] == 0]
# 筛选出含蘑菇的菜品价格列表
mushroom = [dish[0] for dish in dishes if dish[1] == 1]
if len(non_mushroom) < k - t or len(mushroom) < t:
continue
price = sum(non_mushroom[:k - t]) + sum(mushroom[:t])
min_price = min(min_price, price)
return -1 if min_price == float('inf') else min_price
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)
四、总结
通过这个饭馆点菜的问题,我们运用了排序、贪心算法以及简单的枚举技巧来解决实际的优化选择问题。贪心算法在很多类似的需要求最优解且满足一定限制条件的场景中都非常实用,不过使用时要确保每一步的局部最优选择最终能导向全局最优解哦。