• LC-1774. 最接近目标价格的甜点成本(DFS、01背包、三进制状态枚举)


    1774. 最接近目标价格的甜点成本

    难度中等55

    你打算做甜点,现在需要购买配料。目前共有 n 种冰激凌基料和 m 种配料可供选购。而制作甜点需要遵循以下几条规则:

    • 必须选择 一种 冰激凌基料。
    • 可以添加 一种或多种 配料,也可以不添加任何配料。
    • 每种类型的配料 最多两份

    给你以下三个输入:

    • baseCosts ,一个长度为 n 的整数数组,其中每个 baseCosts[i] 表示第 i 种冰激凌基料的价格。
    • toppingCosts,一个长度为 m 的整数数组,其中每个 toppingCosts[i] 表示 一份i 种冰激凌配料的价格。
    • target ,一个整数,表示你制作甜点的目标价格。

    你希望自己做的甜点总成本尽可能接近目标价格 target

    返回最接近 target 的甜点成本。如果有多种方案,返回 成本相对较低 的一种。

    示例 1:

    输入:baseCosts = [1,7], toppingCosts = [3,4], target = 10
    输出:10
    解释:考虑下面的方案组合(所有下标均从 0 开始):
    - 选择 1 号基料:成本 7
    - 选择 1 份 0 号配料:成本 1 x 3 = 3
    - 选择 0 份 1 号配料:成本 0 x 4 = 0
    总成本:7 + 3 + 0 = 10 。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    示例 2:

    输入:baseCosts = [2,3], toppingCosts = [4,5,100], target = 18
    输出:17
    解释:考虑下面的方案组合(所有下标均从 0 开始):
    - 选择 1 号基料:成本 3
    - 选择 1 份 0 号配料:成本 1 x 4 = 4
    - 选择 2 份 1 号配料:成本 2 x 5 = 10
    - 选择 0 份 2 号配料:成本 0 x 100 = 0
    总成本:3 + 4 + 10 + 0 = 17 。不存在总成本为 18 的甜点制作方案。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    示例 3:

    输入:baseCosts = [3,10], toppingCosts = [2,5], target = 9
    输出:8
    解释:可以制作总成本为 8 和 10 的甜点。返回 8 ,因为这是成本更低的方案。
    
    • 1
    • 2
    • 3

    示例 4:

    输入:baseCosts = [10], toppingCosts = [1], target = 1
    输出:10
    解释:注意,你可以选择不添加任何配料,但你必须选择一种基料。
    
    • 1
    • 2
    • 3

    提示:

    • n == baseCosts.length
    • m == toppingCosts.length
    • 1 <= n, m <= 10
    • 1 <= baseCosts[i], toppingCosts[i] <= 104
    • 1 <= target <= 104

    DFS

    DFS得到配料所有可能的成本,然后对每一种基料找到最接近target的。

    class Solution {
        int best = (int)1e9;
        int target;
        public int closestCost(int[] baseCosts, int[] toppingCosts, int target) {
            this.target = target;
            for(int i = 0; i < baseCosts.length; i++){
                dfs(toppingCosts, 0, baseCosts[i]);
            }
            return best;
        }
    
        //暴搜,数据量很小,枚举每个选择就可以
        public void dfs(int[] arr, int idx, int cost){
            int sign = Math.abs(best-target) - Math.abs(cost-target);
            // sign > 0 : 说明当前cost比best离target的差值小
            //(sign == 0 && cost < best) : 成本相同,且成本比best低
            if(sign > 0 || (sign == 0 && cost < best)){
                best = cost;
            }
    
            //剪支以下,如果当前的成本已经大于cost,没有必要再往后就行选择
            //或者已经遍历完,结束
            if(cost >= target || idx == arr.length){
                return;
            }
    
            //每个配料可以选0,1,2份
            for(int k = 0; k < 3; k++){
                dfs(arr, idx+1, cost + arr[idx]*k);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    01背包

    题解:https://leetcode.cn/problems/closest-dessert-cost/solution/di-gui-hui-su-huo-0-1bei-bao-wen-ti-by-w-3hhr/

    动态规划,0-1背包问题来做。看成本选得到,选不到的问题。冰淇凌选且仅选一个,所以每个冰淇凌的成本都能选到。然后配料可以选0-2份,遍历配料,看在这基础上有哪些成本可以取到。然后看能取到的成本中,哪个最接近target。时间复杂度O(n+m⋅20000)。

    public class leetcode5690 {
        // 动态规划,0-1背包问题
        public int closestCost(int[] baseCosts, int[] toppingCosts, int target) {
            int n = baseCosts.length;
            int m = toppingCosts.length;
            // target最多10000,冰淇凌和配料也最多一万,答案不会超过20000
            boolean[] dp = new boolean[20001];
            // 每个冰淇凌只能选一个
            for(int i = 0;i < n;i++){
                dp[baseCosts[i]] = true;
            }
            // 配料可以选0-2份,扩展一倍
            int[] topping = Arrays.copyOf(toppingCosts, m * 2);
            for(int i = m;i < 2 * m;i++){
                topping[i] = topping[i-m];
            }
            // 枚举物品
            for(int i = 0;i < 2 * m;i++){
                int cur = topping[i];
                // 如果前一个成本能选到,加了这个配料的价格后,也能选到
                for(int j = 20000;j > cur;j--){
                    dp[j] = dp[j] || dp[j-cur];
                }
            }
            int ans = 0;
            int dif = 30000;
            // 看可以选到的成本,哪个最接近target
            for(int i = 1;i < 20001;i++){
                int cur = Math.abs(i-target);
                if(dp[i] && cur < dif){
                    dif = cur;
                    ans = i;
                }
            }
            return ans;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    三进制状态压缩

    题解:https://leetcode.cn/problems/closest-dessert-cost/solution/javayong-shi-nei-cun-shuang-100san-jin-z-3jdx/

    考虑到baseCosts、toppingCosts的长度最多都为10,每一种辅料都有加0、1、2份的选择,因此可以考虑三进制状态压缩求解。

    类似二进制的状态压缩。

    class Solution {
        public int closestCost(int[] baseCosts, int[] toppingCosts, int target) {
            Arrays.sort(baseCosts);
            Arrays.sort(toppingCosts);
            int nearestCost=0;//记录最接近的成本
            int minGap = Integer.MAX_VALUE;//记录目前最接近的成本和目标成本target的差
            for(int i=0;i<baseCosts.length;i++){//每一种基料
                for(int j=0;j<Math.pow(3,toppingCosts.length);j++){//三进制位串
                    int cost = baseCosts[i];
                    int curJ = j;//现在的位串
                    int index = 0;
                    while(curJ!=0){//取位串的每一位
                        int curBit = curJ % 3;//位串的当前一位
                        curJ /= 3;
                        cost += toppingCosts[index]*curBit;
                        index++;
                    }
                    if(Math.abs(cost-target)==0){//若现在的成本恰好是target,直接返回
                        return target;
                    }else if(Math.abs(cost-target)<minGap){//若目前的成本更接近target,更新nearestCost和minGap;
                        minGap = Math.abs(cost-target);
                        nearestCost = cost;
                    }else if(Math.abs(cost-target)==minGap){//若目前的成本和已经找到的最接近成本与目标成本的差距相等,取成本较小者(题目要求)
                        if(cost<nearestCost){
                            nearestCost = cost;
                        }
                    }
                }
            }
            return nearestCost;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    补充

    假如一个数的三进制为2 1 2 0 0,则

    int sum = 0;
    int idx = i;
    int cur = 0;
    while(idx!=0){
        sum+=toppingCosts[cur]*(idx%3);
        idx/=3;
        cur++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    则该代码块执行的功能就是 toppingCosts的 [0]·0 + [1]·[0] + [2]·2 + [3]·1 + [4]·2

    枚举完辅料的所有可能后开始枚举主料,依次比较即可。

    • 时间复杂度为 O(n·3^m)。
    • 空间复杂度为 O(1)。
  • 相关阅读:
    如何挑选适合自己的笔记本电脑
    大数据方向面试问题
    吴恩达—机器学习的六个核心算法
    2022软件测试工程师涨薪攻略,3年如何达到30K
    wy的leetcode刷题记录_Day27
    Transform+ASM插桩系列(3)——Transform+ASM的实战
    网络——路由器
    [附源码]java毕业设计幼儿园管理系统
    贪心——122. 买卖股票的最佳时机 II
    docker 使用2台服务器安装 Canal 同步 Mysql 数据
  • 原文地址:https://blog.csdn.net/qq_42958831/article/details/128169094