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

82 阅读6分钟

问题描述

小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

解题思路

首先要明确问题的要求,即在给定的菜品中选择 k 道菜,使得总价格最低,同时满足含有蘑菇的菜品不超过 m 道。

分类:将菜品分为两类,一类是含有蘑菇的菜品,另一类是不含蘑菇的菜品。这可以通过检查字符串 s 中的每个字符来实现,'1' 表示含有蘑菇,'0' 表示不含蘑菇。

排序:为了使总价格尽可能低,需要将两类菜品的价格分别进行排序,这样可以选择价格最低的菜品。

双指针选择:使用两个指针分别指向含有蘑菇和不含蘑菇的菜品列表的开始位置。通过比较两个列表中指针所指向菜品的价格,选择价格较低的菜品加入到最终的选择中,并更新相应的计数器。

条件判断:在选择过程中,需要不断检查是否满足两个条件:一是选择的菜品总数是否达到了 k 道;二是含有蘑菇的菜品数量是否超过了 m 道。如果任一条件不满足,就需要返回 -1。

更新状态:每选择一道菜品,都需要更新总价格、含有蘑菇的菜品数量和已选择的菜品数量。

返回结果:如果成功选择了 k 道菜品且满足蘑菇数量的限制,返回计算出的最小总价格;否则,返回 -1。

思路的核心在于贪心算法的应用,即在每一步选择中都选择当前最优的选项(价格最低的菜品),同时确保满足题目中的约束条件。通过这种方式,可以在多项式时间内找到一个可行的解决方案,尽管这个解决方案不一定是全局最优的,但在这个问题的约束条件下,它是最优的。

算法设计

接收输入参数:菜品的蘑菇标识字符串 s,菜品价格列表 a,含有蘑菇的菜品最大数量 m,需要选择的菜品数量 k

分类存储: 创建两个列表 with_mushroom 和 without_mushroom,分别用于存储含有蘑菇和不含蘑菇的菜品价格。 遍历字符串 s 和价格列表 a,根据 s 中的字符将对应的价格分类存储到两个列表中。

排序: 对 with_mushroom 和 without_mushroom 两个列表进行升序排序,以便于后续选择价格最低的菜品。

初始化变量: 初始化总价格 total_price 为0,含有蘑菇的菜品数 mushroom_count 为0,已选择的菜品数 selected_count 为0。 初始化两个指针 i 和 j 分别指向 with_mushroom 和 without_mushroom 列表的起始位置。

选择菜品: 使用 while 循环进行选择,直到 selected_count 达到 k 或无法继续选择为止。 在每次循环中,比较 with_mushroom[i] 和 without_mushroom[j] 的价格,并根据 mushroom_count 是否小于 m 来决定选择哪个菜品。 如果 with_mushroom[i] 的价格更低且 mushroom_count 小于 m,则选择 with_mushroom[i],否则选择 without_mushroom[j]。 更新 total_pricemushroom_count 和 selected_count

检查条件: 如果在循环结束时 selected_count 小于 k,则说明无法选择足够的菜品,返回 -1。 如果 mushroom_count 大于 m,则说明含有蘑菇的菜品超过了限制,返回 -1。

返回结果:如果成功选择了 k 道菜品且满足蘑菇数量的限制,返回 total_price

代码详解

def solution(s: str, a: list, m: int, k: int) -> int:
    # 将菜品按是否含有蘑菇分类
    with_mushroom = []
    without_mushroom = []
    
    for i in range(len(s)):
        if s[i] == '1':
            with_mushroom.append(a[i])
        else:
            without_mushroom.append(a[i])
    
    # 对两个列表按价格排序
    with_mushroom.sort()
    without_mushroom.sort()
    
    # 初始化变量
    total_price = 0
    mushroom_count = 0
    selected_count = 0
    
    # 从价格最低的菜品开始选择
    i, j = 0, 0
    while selected_count < k:
        if i < len(with_mushroom) and j < len(without_mushroom):
            if with_mushroom[i] < without_mushroom[j] and mushroom_count < m:
                total_price += with_mushroom[i]
                i += 1
                mushroom_count += 1
            else:
                total_price += without_mushroom[j]
                j += 1
        elif i < len(with_mushroom) and mushroom_count < m:
            total_price += with_mushroom[i]
            i += 1
            mushroom_count += 1
        elif j < len(without_mushroom):
            total_price += without_mushroom[j]
            j += 1
        else:
            return -1
        selected_count += 1
    
    # 如果能够选满k道菜并且满足蘑菇的限制,则返回总价格
    # 否则返回-1
    
    return total_price if selected_count == k else -1

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)

总结

通过这道中等题,我可以学习到以下几个重要的编程和算法概念:

贪心算法
  • 贪心算法是一种在每一步选择中都采取当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法策略。
  • 在这个问题中,贪心策略体现在总是优先选择价格较低的菜品,以期望达到总价格最低的目标。
双指针技术
  • 双指针技术常用于处理数组或列表中的问题,通过两个指针的移动来遍历或处理数据。
  • 在这个问题中,使用两个指针分别遍历含有蘑菇和不含蘑菇的菜品列表,以便于比较和选择。
排序算法
  • 排序是算法中的一个基础且重要的概念,它可以帮助我们以特定的顺序处理数据。
  • 在这个问题中,通过排序可以确保我们总是从价格最低的菜品开始选择,这是贪心策略的一部分。
边界条件的处理
  • 学习如何处理各种边界条件,比如当无法选择足够的菜品或者含有蘑菇的菜品超过限制时,应该如何处理。
  • 在这个问题中,当无法满足选择条件时,需要返回 -1 表示无法完成选择。

通过解决这类问题,不仅能够提高编程技能,还能够加深对算法和数据结构的理解,这对于解决更复杂的实际问题是非常有帮助的。