IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> LeetCode 638大礼包 -> 正文阅读

[数据结构与算法]LeetCode 638大礼包

题目

//在 LeetCode 商店中, 有 n 件在售的物品。每件物品都有对应的价格。然而,也有一些大礼包,每个大礼包以优惠的价格捆绑销售一组物品。
//
// 给你一个整数数组 price 表示物品价格,其中 price[i] 是第 i 件物品的价格。另有一个整数数组 needs 表示购物清单,其中 needs[
//i] 是需要购买第 i 件物品的数量。
//
// 还有一个数组 special 表示大礼包,special[i] 的长度为 n + 1 ,其中 special[i][j] 表示第 i 个大礼包中内含第 j
// 件物品的数量,且 special[i][n] (也就是数组中的最后一个整数)为第 i 个大礼包的价格。
//
// 返回 确切 满足购物清单所需花费的最低价格,你可以充分利用大礼包的优惠活动。你不能购买超出购物清单指定数量的物品,即使那样会降低整体价格。任意大礼包可无限
//次购买。
//
//
//
// 示例 1:
//
//
//输入:price = [2,5], special = [[3,0,5],[1,2,10]], needs = [3,2]
//输出:14
//解释:有 A 和 B 两种物品,价格分别为 ¥2 和 ¥5 。
//大礼包 1 ,你可以以 ¥5 的价格购买 3A 和 0B 。
//大礼包 2 ,你可以以 ¥10 的价格购买 1A 和 2B 。
//需要购买 3 个 A 和 2 个 B , 所以付 ¥10 购买 1A 和 2B(大礼包 2),以及 ¥4 购买 2A 。
//
// 示例 2:
//
//
//输入:price = [2,3,4], special = [[1,1,0,4],[2,2,1,9]], needs = [1,2,1]
//输出:11
//解释:A ,B ,C 的价格分别为 ¥2 ,¥3 ,¥4 。
//可以用 ¥4 购买 1A 和 1B ,也可以用 ¥9 购买 2A ,2B 和 1C 。
//需要买 1A ,2B 和 1C ,所以付 ¥4 买 1A 和 1B(大礼包 1),以及 ¥3 购买 1B , ¥4 购买 1C 。
//不可以购买超出待购清单的物品,尽管购买大礼包 2 更加便宜。
//
//
//
// 提示:
//
//
// n == price.length
// n == needs.length
// 1 <= n <= 6
// 0 <= price[i] <= 10
// 0 <= needs[i] <= 10
// 1 <= special.length <= 100
// special[i].length == n + 1
// 0 <= special[i][j] <= 50

思路

其实本题思路很简单。就是暴力搜索。选或者不选大礼包,最终取一个最小值。

代码

jint res = 0;

        public int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {

            //price = [2,5], special = [[3,0,5],[1,2,10]], needs = [3,2]
            // 不选任何大礼包,做为初始值。
            res = noSpecial(price, needs);
            backTrace(price, special, needs, 0, 0);
            return res;
        }

        private int noSpecial(List<Integer> price, List<Integer> needs) {
            int sum = 0;
            for (int i = 0; i < needs.size(); i++) {
                sum += price.get(i) * needs.get(i);

            }
            return sum;
        }

        // 枚举所有可能的大礼包
        private void backTrace(List<Integer> price, List<List<Integer>> special, List<Integer> needs, int index,
                               int cost) {
            if (res < cost) {
                return;
            }

            // special 使用完了,如果 needs还没有满足,则需要直接使用。
            if (index == special.size()) {
                cost += noSpecial(price, needs);
                res = Math.min(cost, res);
                return;
            }


            List<Integer> tmpSpecial = special.get(index);
            //检查此大礼包中的每一个礼物都符合要求,才能选择该大礼包
            if (checkValidate(tmpSpecial, needs)) {
                // 此大礼包可以选择,需要更新needs以及总共的花费。
                List<Integer> newNeed = new ArrayList<>();
                for (int j = 0; j < needs.size(); j++) {
                    newNeed.add(needs.get(j) - tmpSpecial.get(j));

                }
                backTrace(price, special, newNeed, index, cost + tmpSpecial.get(tmpSpecial.size() - 1));

            }
            // 注意,这个地方与前面的if并不是if-else关系。无论前面能不能使用大礼包,这里都必须有。并不是前面能用大礼包了,这里就不需要了。
            // 这个是枚举所有可能,并不是贪心。
            backTrace(price, special, needs, index + 1, cost);

        }

        private boolean checkValidate(List<Integer> tmpSpecial, List<Integer> needs) {
            for (int j = 0; j < needs.size(); j++) {
                if (tmpSpecial.get(j) > needs.get(j)) {
                    return false;

                }
            }
            return true;
        }

代码2:回溯。这种方法与常规的套路一样。很容易想出来。

public int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {

            //price = [2,5], special = [[3,0,5],[1,2,10]], needs = [3,2]
            // 不选任何大礼包,做为初始值。


            return backTrace(price, special, needs);
        }

        private int noSpecial(List<Integer> price, List<Integer> needs) {
            int sum = 0;
            for (int i = 0; i < needs.size(); i++) {
                sum += price.get(i) * needs.get(i);

            }
            return sum;
        }

        // 枚举所有可能的大礼包
        private int backTrace(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {

            int noSpecial = noSpecial(price, needs);
            int n = special.size();
            for (int i = 0; i < n; i++) {
                List<Integer> tmpSpecial = special.get(i);
                if (checkValidate(tmpSpecial, needs)) {

                    //设置值
                    for (int j = 0; j < needs.size(); j++) {
                        needs.set(j, needs.get(j) - tmpSpecial.get(j));
                    }
                    noSpecial = Math.min(backTrace(price, special, needs) + tmpSpecial.get(tmpSpecial.size() - 1), noSpecial);
                    //恢复值
                    for (int j = 0; j <needs.size(); j++) {
                        needs.set(j, needs.get(j) + tmpSpecial.get(j));
                    }
                }
            }
            return noSpecial;

        }

        
        private boolean checkValidate(List<Integer> tmpSpecial, List<Integer> needs) {
            for (int j = 0; j < needs.size(); j++) {
                if (tmpSpecial.get(j) > needs.get(j)) {
                    return false;

                }
            }
            return true;
        }
    }

总结

从这个题可以看出递归与回溯的区别

递归: 一条路走到底
回溯: 一条路走到底,不对时可以重新选择。因此多了一个撤销的过程。

backTrace(){
//1 . 终止条
//3. for (选择){
// 处理某种变量
backTrace()
// 复原某种变量
}
}

递归其实只是回溯其中的一步。
其实本题本质就是回溯。上面虽然看着是dfs,其实最后一步就类似于一个for循环,不过只有2个元素。
if (checkValidate(tmpSpecial, needs)) //类似于设置值
 backTrace(price, special, needs, index + 1, cost); //类似于撤销之前的处理。


  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-09-13 09:31:11  更:2021-09-13 09:31:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 14:37:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码