饭馆菜品选择问题

63 阅读3分钟

问题描述

小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。

约束条件:

  1. 菜按是否含有蘑菇分为两类:s[i] = '1'(含蘑菇)和 s[i] = '0'(不含蘑菇)。
  2. 按照价格升序选择,保证总价格最低。

解题思路

  1. 将菜品分类
    根据 s 的值,将含有蘑菇的菜品与不含蘑菇的菜品分成两组,分别存入两个列表:

    • mushroomDishes:存放含有蘑菇的菜品价格;
    • nonMushroomDishes:存放不含蘑菇的菜品价格。
  2. 排序
    对两组菜品分别按价格升序排列。

  3. 枚举含蘑菇菜品的数量
    通过遍历 0 到 min(k,m),尝试选取不同数量的含蘑菇菜品:

    • 如果选取 mushroomCount 道含蘑菇的菜,则需要从不含蘑菇菜品中补齐 nonMushroomCount=k−mushroomCount道;
    • 如果 nonMushroomCount>nonMushroomDishes.size(),说明无法满足,跳过。
  4. 计算当前组合的价格
    选取价格最低的 mushroomCount 道含蘑菇菜和 nonMushroomCount 道不含蘑菇菜,求和。

  5. 记录最小总价格
    通过遍历所有可能的组合,找到总价格最低的选择。

  6. 特殊情况

    • 如果 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

  1. 分类菜品

    • 不含蘑菇的菜:nonMushroomDishes = [10, 20]
    • 含蘑菇的菜:mushroomDishes = [30]
  2. 排序

    • 排序后不变:nonMushroomDishes = [10, 20], mushroomDishes = [30]
  3. 枚举蘑菇菜数量

    • 蘑菇菜数量为 0:选不含蘑菇菜 2 道,总价格为 10 + 20 = 30
    • 蘑菇菜数量为 1:需要选不含蘑菇菜 1 道,但不可能满足 k=2k = 2k=2。
  4. 输出结果
    最小总价格为 30。


时间复杂度分析

  • 分类菜品:O(n)
  • 排序:O(nlogn)
  • 枚举组合:O(m),其中 m≤km
  • 总复杂度:O(nlogn+m)
    在实际情况中,m 和 k 通常很小,主导部分为排序。