问题背景
小C来到了一家饭馆,餐馆有n道菜,其中每道菜的价格和是否含有蘑菇的信息都已知。小C希望点k道菜,并且希望总价格尽可能低。同时,她不喜欢蘑菇,因此在所选的k道菜中,最多只能包含m道含有蘑菇的菜。我们需要计算出在满足这些条件下,小C能够选择的最小总价格。如果无法满足条件,则输出-1。
问题分析
这个问题本质上是一个带有约束的最小化问题。我们需要在满足“最多选择m道含有蘑菇的菜”和“选择k道菜”的前提下,找到总价格最小的组合。以下是我们在解决问题时需要考虑的关键点:
-
选择菜品的限制条件:
- 小C最多只能选择m道含有蘑菇的菜,这限制了我们能选择的蘑菇菜的数量。
- 小C需要选k道菜,因此这也限制了我们总共需要选出的菜品数。
-
菜品的优先选择:
- 为了使总价格最小,我们需要优先选择价格较低的菜。
- 由于菜品的蘑菇含量和价格都不同,我们需要分别处理含蘑菇菜和不含蘑菇菜,以满足题目中对蘑菇菜数量的限制。
算法思路
-
分类菜品: 我们将所有的菜按是否含有蘑菇分成两类:
- 含蘑菇的菜。
- 不含蘑菇的菜。
-
排序: 对于每一类菜品,我们都需要按价格从低到高排序,目的是优先选择价格最便宜的菜。
-
选择策略:
- 我们需要选择的菜品数为k个,同时需要满足“最多选择m道蘑菇菜”的限制。
- 遍历所有可能的蘑菇菜数量(从0道到m道),并尝试在满足条件的情况下,选择价格最小的k道菜。
-
边界情况: 如果我们无法从含蘑菇和不含蘑菇的菜中选出k道菜,或者选出的蘑菇菜数量超过了m道,我们就返回-1。
步骤详解
-
初始化:
- 我们将含蘑菇的菜和不含蘑菇的菜分别提取出来,并对它们的价格进行排序。
-
遍历所有可能的蘑菇菜数量:
- 对于每种可能的蘑菇菜数量,我们从含蘑菇的菜和不含蘑菇的菜中选择最便宜的菜,并计算出总价格。
-
检查有效性:
- 如果在某种蘑菇菜数量的情况下,能选出k道菜且满足条件,则计算并记录最小价格。
-
返回结果:
- 最后返回计算出的最小总价格,如果找不到有效的选择,则返回-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;
}
代码解析
-
分类菜品:
- 使用字符串
s判断菜品是否含蘑菇,将菜品价格分成两个数组:一个是含蘑菇的菜品数组mushrooms,另一个是非蘑菇的菜品数组non_mushrooms。
- 使用字符串
-
排序:
- 分别对这两个数组进行排序,以便从价格最低的菜开始选择。
-
遍历蘑菇菜数量:
- 对于每个可能的蘑菇菜数量(从0到m),尝试选择k道菜并计算总价格。
-
计算最小价格:
- 如果找到了有效的菜品组合,就更新最小价格。
-
返回结果:
- 如果能够找到符合条件的菜品组合,返回最小总价格;否则,返回-1。
时间复杂度分析
- 分类菜品: 这部分的时间复杂度为O(n),因为我们需要遍历一遍数组将菜品分成两类。
- 排序: 对两类菜品分别进行排序,时间复杂度为O(n log n)。
- 遍历可能的蘑菇菜数量: 由于我们遍历蘑菇菜数量的上限为m,因此这个部分的时间复杂度为O(m)。
总体时间复杂度为O(n log n + m),其中n是菜品数,m是蘑菇菜的最大数量。对于大多数输入规模,这个复杂度是可以接受的。
总结
本题的核心在于如何在两类菜品中做出最优选择,保证价格最小且符合蘑菇菜数量的限制。通过将菜品按价格排序并结合枚举方法,我们能够高效地解决这个问题。同时,通过合理处理边界条件和遍历所有可能的蘑菇菜数量,我们确保了算法的正确性和全面性。