题目:饭馆菜品选择问题
选题原因:具有现实意义,与日常生活联系较深。
问题描述
小C来到了一家饭馆,这里共有S3ns 道菜,第SiS 道菜的价格为a_1。其中一些菜中含有藤菇,5_1 代表第SiS 道菜是否含有脑菇。如果S_i =1,那么第 $iS道菜含有蘑菇,否则没有。
小C希望点SKS 道菜,且希望总价格尽可能低。由于她不喜欢蘑菇,她希望所点的菜中最多只有Sms 道菜含有蘑菇。小C想知道在满足条件的情况下能选出的最小总价格是多少。如果无法按照要求选择菜品,则输出-1.
测试样例
样例1:
|输入:s ="801", a= [10, 20, 30], m=1, k=2输出:30
样例2:
|输入:s = "111", a =[10, 20, 30], m = 1, k=2输出:-1
样例3.
|输入:s = "0181",a = [5, 15, 10,20], m = 2, k=3输出:30
分析:首先,这是一个组合优化的问题,需要满足一定条件下,找到最小总价格。那么可以考虑使用动态规划或者贪心算法或者枚举来解决。
这段代码使用的是暴力枚举(穷举)的思路来解决问题,具体分析如下: 1. 生成菜品信息列表 - 首先通过dishes = [(price, has_mushroom) for price, has_mushroom in zip(a, map(int, s))]将价格列表a和表示是否含蘑菇的字符串列表s(先转换为整数)组合成一个包含菜品价格和是否含蘑菇信息的二元组列表dishes。
2. 枚举所有可能的组合 - 使用for i in range(2**n)来生成从0到2^n - 1的所有整数,这里的n是菜品的数量。每个整数i的二进制表示可以看作是一种菜品选择的组合方式,其中二进制位为1表示选择该菜品,为0表示不选择。 - 对于每个整数i,通过内层循环for j in range(n)来检查i的每一位,如果i & (1 << j)不为0,则表示选择了第j个菜品,将其加入到当前组合combination中,并更新含蘑菇菜品数量mushroom_count和总价格total_price。
3. 3. 筛选符合条件的组合并更新最小价格 - 在生成一个组合后,检查组合中的菜品数量是否等于k且含蘑菇菜品数量是否小于等于m。如果满足条件,则更新最小价格min_price,取当前组合价格total_price和之前最小价格min_price中的较小值。
4. 返回结果 - 最后,如果最小价格小于正无穷(表示找到了符合条件的组合),则返回最小价格,否则返回-1表示无法按照要求选择菜品。
def solution(n):
result = []
for i in range(1, n + 1):
# 从n到i逆序生成序列,并添加到结果列表中
result.extend(range(n, i - 1, -1))
return result
示例
n = 3
output = solution(n)
print(output) # 输出应该是 [3, 2, 1, 3, 2, 3]
总结:这种暴力枚举的方法虽然能够找到正确答案,但时间复杂度较高,其中n是菜品数量。当菜品数量较大时,计算量会呈指数级增长,效率较低。对于大规模数据可能需要考虑更优化的算法,如动态规划或贪心算法(如前面分析的贪心算法)等。
当然也可以用动态规划的思路:
一、动态规划思路 1. 定义状态 - 设dp[i][j][p]表示考虑前i道菜,选了j道菜,其中有p道菜含有蘑菇时的最小总价格。
- 状态转移方程 - 当不选第
i道菜时:dp[i][j][p] = dp[i - 1][j][p]。 - 当选第i道菜时:dp[i][j][p] = min(dp[i][j][p], dp[i - 1][j - 1][p - s[i]] + a[i]),这里s[i]表示第i道菜是否含蘑菇(1 表示含,0 表示不含),a[i]表示第i道菜的价格。需要注意的是,要确保j > 0且p >= s[i]时才能进行这种转移。 -
- 初始化 - 当
j = 0且p = 0时,dp[i][0][0] = 0,表示不选任何菜时总价格为 0。其他情况初始化为正无穷。
- 初始化 - 当
-
- 计算顺序 - 按照
i从 1 到n(菜品数量),j从 1 到k(要选的菜数量),p从 0 到m(含蘑菇菜数量限制)的顺序进行计算。
- 计算顺序 - 按照
-
- 最终结果 - 遍历
dp[n][k][p](p从 0 到m),找到其中的最小值作为最终结果。如果最小值为正无穷,则表示无法按照要求选择菜品,返回 -1。 以下是使用实现的代码:
- 最终结果 - 遍历
- def solution(s, a, m, k): n = len(a) # 将表示是否含蘑菇的字符串列表转换为整数列表 s = list(map(int, s)) no_mushroom_dishes = [(price, i) for i, (price, has_mushroom) in enumerate(zip(a, s)) if has_mushroom == 0] mushroom_dishes = [(price, i) for i, (price, has_mushroom) in enumerate(zip(a, s)) if has_mushroom == 1] no_mushroom_dishes.sort() mushroom_dishes.sort() total_price = 0 mushroom_count = 0 # 先选不含蘑菇的菜品 for i in range(min(len(no_mushroom_dishes), k)): total_price += no_mushroom_dishes[i][0] # 如果不含蘑菇的菜品选不够 k 个,再选含蘑菇的菜品 if len(no_mushroom_dishes) < k: remaining = k - len(no_mushroom_dishes) if remaining > m: return -1 for i in range(min(len(mushroom_dishes), remaining)): total_price += mushroom_dishes[i][0] mushroom_count += 1 return total_price if mushroom_count <= m else -1 该代码的不足之处就是长。 总结: 动态规划的时间复杂度为,空间复杂度为(可以通过优化将空间复杂度降低到)。贪心算法的时间复杂度为(主要是排序的时间),空间复杂度为。相比之前的暴力枚举算法(时间复杂度为),动态规划和贪心算法在效率上有较大提升,尤其是对于大规模数据,贪心算法在满足条件时效率更高且实现相对简单。
`