问题描述
小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
题目解析
题目要求:
- 小C需要从 nnn 道菜中挑选 kkk 道,总价格尽可能低;
- 最多只能包含 mmm 道含有蘑菇的菜;
- 如果无法满足条件,输出 −1-1−1。
约束条件:
- 菜按是否含有蘑菇分为两类:
s[i] = '1'(含蘑菇)和s[i] = '0'(不含蘑菇)。 - 按照价格升序选择,保证总价格最低。
解题思路
-
将菜品分类
根据s的值,将含有蘑菇的菜品与不含蘑菇的菜品分成两组,分别存入两个列表:mushroomDishes:存放含有蘑菇的菜品价格;nonMushroomDishes:存放不含蘑菇的菜品价格。
-
排序
对两组菜品分别按价格升序排列。 -
枚举含蘑菇菜品的数量
通过遍历 0 到 min(k,m),尝试选取不同数量的含蘑菇菜品:- 如果选取 mushroomCount 道含蘑菇的菜,则需要从不含蘑菇菜品中补齐 nonMushroomCount=k−mushroomCount道;
- 如果 nonMushroomCount>nonMushroomDishes.size(),说明无法满足,跳过。
-
计算当前组合的价格
选取价格最低的 mushroomCount 道含蘑菇菜和 nonMushroomCount 道不含蘑菇菜,求和。 -
记录最小总价格
通过遍历所有可能的组合,找到总价格最低的选择。 -
特殊情况
- 如果 k>total dishesk,直接返回 −1;
- 如果无法满足所有条件,返回−1。
代码详解
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static long solution(String s, int[] a, int m, int k) {
// 用于存储不含蘑菇和含蘑菇的菜品价格
List<Integer> nonMushroomDishes = new ArrayList<>();
List<Integer> mushroomDishes = new ArrayList<>();
// Step 1: 分类菜品
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '0') {
nonMushroomDishes.add(a[i]);
} else {
mushroomDishes.add(a[i]);
}
}
// Step 2: 排序价格
Collections.sort(nonMushroomDishes);
Collections.sort(mushroomDishes);
// Edge case: 如果菜品总数不足 k
if (nonMushroomDishes.size() + mushroomDishes.size() < k) {
return -1;
}
long minTotalPrice = Long.MAX_VALUE; // 初始化为最大值
// Step 3: 枚举含蘑菇菜的数量
for (int mushroomCount = 0; mushroomCount <= m && mushroomCount <= mushroomDishes.size(); mushroomCount++) {
int nonMushroomCount = k - mushroomCount; // 剩余需要的不含蘑菇菜数量
// 如果不含蘑菇的菜数量不足
if (nonMushroomCount > nonMushroomDishes.size()) {
continue;
}
// 计算当前组合的总价格
long totalPrice = 0;
for (int i = 0; i < mushroomCount; i++) {
totalPrice += mushroomDishes.get(i);
}
for (int i = 0; i < nonMushroomCount; i++) {
totalPrice += nonMushroomDishes.get(i);
}
// 更新最小总价格
minTotalPrice = Math.min(minTotalPrice, totalPrice);
}
// 如果没有找到有效组合,返回 -1
return minTotalPrice == Long.MAX_VALUE ? -1 : minTotalPrice;
}
public static void main(String[] args) {
System.out.println(solution("001", new int[]{10, 20, 30}, 1, 2) == 30);
System.out.println(solution("111", new int[]{10, 20, 30}, 1, 2) == -1);
System.out.println(solution("0101", new int[]{5, 15, 10, 20}, 2, 3) == 30);
}
}
运行流程图解
以样例 1 为例:
输入:s = "001", a = [10, 20, 30], m = 1, k = 2
-
分类菜品
- 不含蘑菇的菜:
nonMushroomDishes = [10, 20] - 含蘑菇的菜:
mushroomDishes = [30]
- 不含蘑菇的菜:
-
排序
- 排序后不变:
nonMushroomDishes = [10, 20], mushroomDishes = [30]
- 排序后不变:
-
枚举蘑菇菜数量
- 蘑菇菜数量为 0:选不含蘑菇菜 2 道,总价格为
10 + 20 = 30; - 蘑菇菜数量为 1:需要选不含蘑菇菜 1 道,但不可能满足 k=2k = 2k=2。
- 蘑菇菜数量为 0:选不含蘑菇菜 2 道,总价格为
-
输出结果
最小总价格为 30。
时间复杂度分析
- 分类菜品:O(n)
- 排序:O(nlogn)
- 枚举组合:O(m),其中 m≤km
- 总复杂度:O(nlogn+m)
在实际情况中,m 和 k 通常很小,主导部分为排序。