36.求解餐馆点菜最小价格问题 | 豆包MarsCode AI刷题

76 阅读5分钟

问题背景

小C来到了一家饭馆,餐馆有n道菜,其中每道菜的价格和是否含有蘑菇的信息都已知。小C希望点k道菜,并且希望总价格尽可能低。同时,她不喜欢蘑菇,因此在所选的k道菜中,最多只能包含m道含有蘑菇的菜。我们需要计算出在满足这些条件下,小C能够选择的最小总价格。如果无法满足条件,则输出-1。

问题分析

这个问题本质上是一个带有约束的最小化问题。我们需要在满足“最多选择m道含有蘑菇的菜”和“选择k道菜”的前提下,找到总价格最小的组合。以下是我们在解决问题时需要考虑的关键点:

  1. 选择菜品的限制条件:

    • 小C最多只能选择m道含有蘑菇的菜,这限制了我们能选择的蘑菇菜的数量。
    • 小C需要选k道菜,因此这也限制了我们总共需要选出的菜品数。
  2. 菜品的优先选择:

    • 为了使总价格最小,我们需要优先选择价格较低的菜。
    • 由于菜品的蘑菇含量和价格都不同,我们需要分别处理含蘑菇菜和不含蘑菇菜,以满足题目中对蘑菇菜数量的限制。

算法思路

  1. 分类菜品: 我们将所有的菜按是否含有蘑菇分成两类:

    • 含蘑菇的菜。
    • 不含蘑菇的菜。
  2. 排序: 对于每一类菜品,我们都需要按价格从低到高排序,目的是优先选择价格最便宜的菜。

  3. 选择策略:

    • 我们需要选择的菜品数为k个,同时需要满足“最多选择m道蘑菇菜”的限制。
    • 遍历所有可能的蘑菇菜数量(从0道到m道),并尝试在满足条件的情况下,选择价格最小的k道菜。
  4. 边界情况: 如果我们无法从含蘑菇和不含蘑菇的菜中选出k道菜,或者选出的蘑菇菜数量超过了m道,我们就返回-1。

步骤详解

  1. 初始化:

    • 我们将含蘑菇的菜和不含蘑菇的菜分别提取出来,并对它们的价格进行排序。
  2. 遍历所有可能的蘑菇菜数量:

    • 对于每种可能的蘑菇菜数量,我们从含蘑菇的菜和不含蘑菇的菜中选择最便宜的菜,并计算出总价格。
  3. 检查有效性:

    • 如果在某种蘑菇菜数量的情况下,能选出k道菜且满足条件,则计算并记录最小价格。
  4. 返回结果:

    • 最后返回计算出的最小总价格,如果找不到有效的选择,则返回-1。

代码实现

  #include <iostream>
  #include <vector>
  #include <algorithm>
  #include <string>int solution(std::string s, std::vector<int> a, int m, int k) {
      // 分离含蘑菇和不含蘑菇的菜品
      std::vector<int> mushrooms, non_mushrooms;
      for (int i = 0; i < s.size(); ++i) {
          if (s[i] == '1') {
              mushrooms.push_back(a[i]);
          } else {
              non_mushrooms.push_back(a[i]);
          }
      }
      
      // 排序价格从低到高
      std::sort(mushrooms.begin(), mushrooms.end());
      std::sort(non_mushrooms.begin(), non_mushrooms.end());
      
      int min_price = INT_MAX;
      
      // 遍历所有可能的蘑菇菜数量
      for (int i = 0; i <= std::min(m, (int)mushrooms.size()); ++i) {
          if (k - i > non_mushrooms.size()) {
              continue;  // 如果剩余菜品数不足,则跳过
          }
          
          // 选i道蘑菇菜和k-i道不含蘑菇的菜
          int total_price = 0;
          
          // 计算蘑菇菜的价格
          for (int j = 0; j < i; ++j) {
              total_price += mushrooms[j];
          }
          
          // 计算不含蘑菇菜的价格
          for (int j = 0; j < k - i; ++j) {
              total_price += non_mushrooms[j];
          }
          
          // 更新最小总价格
          min_price = std::min(min_price, total_price);
      }
      
      // 如果找不到合法的选择,返回-1
      return (min_price == INT_MAX) ? -1 : min_price;
  }
  ​
  int main() {
      std::string s1 = "001";
      std::vector<int> a1 = {10, 20, 30};
      int m1 = 1, k1 = 2;
      std::cout << solution(s1, a1, m1, k1) << std::endl;  // 输出30
      
      std::string s2 = "111";
      std::vector<int> a2 = {10, 20, 30};
      int m2 = 1, k2 = 2;
      std::cout << solution(s2, a2, m2, k2) << std::endl;  // 输出-1
      
      std::string s3 = "0101";
      std::vector<int> a3 = {5, 15, 10, 20};
      int m3 = 2, k3 = 3;
      std::cout << solution(s3, a3, m3, k3) << std::endl;  // 输出30
      
      return 0;
  }

代码解析

  1. 分类菜品:

    • 使用字符串 s 判断菜品是否含蘑菇,将菜品价格分成两个数组:一个是含蘑菇的菜品数组 mushrooms,另一个是非蘑菇的菜品数组 non_mushrooms
  2. 排序:

    • 分别对这两个数组进行排序,以便从价格最低的菜开始选择。
  3. 遍历蘑菇菜数量:

    • 对于每个可能的蘑菇菜数量(从0到m),尝试选择k道菜并计算总价格。
  4. 计算最小价格:

    • 如果找到了有效的菜品组合,就更新最小价格。
  5. 返回结果:

    • 如果能够找到符合条件的菜品组合,返回最小总价格;否则,返回-1。

时间复杂度分析

  1. 分类菜品: 这部分的时间复杂度为O(n),因为我们需要遍历一遍数组将菜品分成两类。
  2. 排序: 对两类菜品分别进行排序,时间复杂度为O(n log n)。
  3. 遍历可能的蘑菇菜数量: 由于我们遍历蘑菇菜数量的上限为m,因此这个部分的时间复杂度为O(m)。

总体时间复杂度为O(n log n + m),其中n是菜品数,m是蘑菇菜的最大数量。对于大多数输入规模,这个复杂度是可以接受的。

总结

本题的核心在于如何在两类菜品中做出最优选择,保证价格最小且符合蘑菇菜数量的限制。通过将菜品按价格排序并结合枚举方法,我们能够高效地解决这个问题。同时,通过合理处理边界条件和遍历所有可能的蘑菇菜数量,我们确保了算法的正确性和全面性。