在 LeetCode 商店中, 有 n 件在售的物品。每件物品都有对应的价格。然而,也有一些大礼包,每个大礼包以优惠的价格捆绑销售一组物品。

给你一个整数数组 price 表示物品价格,其中 price[i] 是第 i 件物品的价格。另有一个整数数组 needs 表示购物清单,其中 needs[i] 是需要购买第 i 件物品的数量。

还有一个数组 special 表示大礼包,special[i] 的长度为 n + 1 ,其中 special[i][j] 表示第 i 个大礼包中内含第 j 件物品的数量,且 special[i][n] (也就是数组中的最后一个整数)为第 i 个大礼包的价格。

返回确切满足足购物清单所需花费的最低价格,你可以充分利用大礼包的优惠活动。你不能购买超出购物清单指定数量的物品,即使那样会降低整体价格。任意大礼包可无限次购买。

解题思路:

由于购买礼包一定比单个购买要划算,因此要想所花金额最少,一定要尽量购买礼包。同时题目还要求不能让购买的商品总数超过需求量,这减小了题目的难度。本题可以使用回溯思想,将所有尽可能买礼包的情况都计算一遍,比较得所花的最少金额。

假设一共有n个礼包,编号为:0,1,2……n-1,则思路为:

1.先根据商品集合needs,购买尽可能多的0号礼包(假设购买量为quantity[0]),并更新needs;之后根据更新后的needs购买尽可能多的1号礼包(假设购买量为quantity[1]),并更新needs……一直到最后尽可能多的把n-1号礼包都购买好后,再把needs剩余的部分(这些部分无法再用礼包购买)单独购买,算出一次的金额。

2.之后回溯到购买n-2号商品的情况,将n-2后商品的购买量减去1(此时购买数量为quantity[n-2]=quantity[n-2]-1),之后再尽可能多的购买n-1号商品又可以算出一次金额。之后再让n-2号商品的购买量再次减1(quantity[n-2]=quantity[n-2]-1),就这样直到n-2号商品的购买量到0位置。

3.接下来回溯到n-3号商品的,与2类似,每次令quantity[n-3]=quantity[n-3]-1,然后尽可能多的购买n-2号和n-1号商品(n-3号商品购买量每减少1,就要重复一次第2步考察所有尽可能多的购买n-2号商品和n-1号商品的情况)。之后再不断回溯到低n-4,n-5直到0号商品,这样就可以比较得出所有金额中最小的数值。

代码如下:

   int res = Integer.MAX_VALUE;public int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {//优先使用大礼包getRes(price, special, needs, 0, 0);//从第0号礼包开始使用return res;}/*** @param price* @param special* @param needs* @param specialNo 代表现在需要使用的礼包的下标* @param total     至今为止总共花费的钱*/private void getRes(List<Integer> price, List<List<Integer>> special, List<Integer> needs, int specialNo, int total) {List<Integer> specialPack = special.get(specialNo);int specialQuantity = getSpecialQuantity(special, needs, specialNo);//根据needs获取specialNo编号的大礼包最多使用几个for (int i = specialQuantity; i >= 0; i--) {int newTotal = total;ArrayList<Integer> newNeeds = new ArrayList<>(needs);//复制一份needsupdataNewNeeds(newNeeds, i, specialPack);//使用specialNo号的礼包后,更新newNeedsnewTotal += i * specialPack.get(specialPack.size() - 1);//更新total的金额数if (specialNo + 1 <= special.size() - 1) getRes(price, special, newNeeds, specialNo + 1, newTotal);if (specialNo == special.size() - 1) {//此时已经将最后一个礼包使用结束了,剩余部分就应该单个购买int sum = newTotal + getSumFromPrice(price, newNeeds);res = Math.min(res, sum);}}}private void updataNewNeeds(ArrayList<Integer> newNeeds, int speciaQuantity, List<Integer> specialPack) {for (int i = 0; i < newNeeds.size(); i++) {newNeeds.set(i, newNeeds.get(i) - speciaQuantity * specialPack.get(i));}}//获取specialNo编号的大礼包最多可以使用几个private int getSpecialQuantity(List<List<Integer>> special, List<Integer> newNeeds, int specialNo) {int quantity = Integer.MAX_VALUE;List<Integer> specialPack = special.get(specialNo);//通过礼包编号确定礼包for (int i = 0; i < newNeeds.size(); i++) {if (specialPack.get(i) != 0) {int temp = newNeeds.get(i) / specialPack.get(i);if (temp == 0) return 0;//代表礼包中的商品大于所需商品,不能买这个礼包quantity = Math.min(temp, quantity);}}if (quantity == Integer.MAX_VALUE) quantity = 0;return quantity;}//不能使用大礼包后,只能从price中计算金额private int getSumFromPrice(List<Integer> price, List<Integer> needs) {int sum = 0;for (int i = 0; i < price.size(); i++) {sum += needs.get(i) * price.get(i);}return sum;}

leetcode刷题 638大礼包相关推荐

  1. C#LeetCode刷题-动态规划

    动态规划篇 # 题名 刷题 通过率 难度 5 最长回文子串 22.4% 中等 10 正则表达式匹配 18.8% 困难 32 最长有效括号 23.3% 困难 44 通配符匹配 17.7% 困难 53 最 ...

  2. LeetCode刷题记录15——21. Merge Two Sorted Lists(easy)

    LeetCode刷题记录15--21. Merge Two Sorted Lists(easy) 目录 LeetCode刷题记录15--21. Merge Two Sorted Lists(easy) ...

  3. LeetCode刷题记录14——257. Binary Tree Paths(easy)

    LeetCode刷题记录14--257. Binary Tree Paths(easy) 目录 前言 题目 语言 思路 源码 后记 前言 数据结构感觉理论简单,实践起来很困难. 题目 给定一个二叉树, ...

  4. LeetCode刷题记录13——705. Design HashSet(easy)

    LeetCode刷题记录13--705. Design HashSet(easy) 目录 LeetCode刷题记录13--705. Design HashSet(easy) 前言 题目 语言 思路 源 ...

  5. LeetCode刷题记录12——232. Implement Queue using Stacks(easy)

    LeetCode刷题记录12--232. Implement Queue using Stacks(easy) 目录 LeetCode刷题记录12--232. Implement Queue usin ...

  6. LeetCode刷题记录11——290. Word Pattern(easy)

    LeetCode刷题记录11--290. Word Pattern(easy) 目录 LeetCode刷题记录11--290. Word Pattern(easy) 题目 语言 思路 源码 后记 题目 ...

  7. LeetCode刷题记录10——434. Number of Segments in a String(easy)

    LeetCode刷题记录10--434. Number of Segments in a String(easy) 目录 LeetCode刷题记录9--434. Number of Segments ...

  8. LeetCode刷题记录9——58. Length of Last Word(easy)

    LeetCode刷题记录9--58. Length of Last Word(easy) 目录 LeetCode刷题记录9--58. Length of Last Word(easy) 题目 语言 思 ...

  9. LeetCode刷题记录8——605. Can Place Flowers(easy)

    LeetCode刷题记录8--605. Can Place Flowers(easy) 目录 LeetCode刷题记录8--605. Can Place Flowers(easy) 题目 语言 思路 ...

最新文章

  1. Linux查看网卡状态
  2. python 底层原理_Python 探针实现原理
  3. 山西省能源产业发展动向及投资风险分析报告2021版
  4. python中set集合如何决定是否重复?
  5. 从零写一个编译器(一):输入系统和词法分析
  6. PAT_B_1015_C++(25分)
  7. UML造型——使用EA时序图工具的开发实践和经验
  8. php 求两个数组的差集应该注意的事情
  9. 吴恩达机器学习(十五)大规模机器学习(Batch、Stochastic、Mini-batch gradient descent、MapReduce)
  10. html js 处理元素的数据属性(data)
  11. RBF神经网络——案例一
  12. 收款收据设计html,最新收款收据模板的格式
  13. PyCharm - Compare With... (文件比较)
  14. 谷歌翻译 翻译文档爬虫
  15. django中ModelForm多表单组合的解决方案
  16. 我想给我的公众号改个名,好不好嘞
  17. 仙人掌相关问题的处理方法(未完待续)
  18. mysql sysdatabases_未能在 sysdatabases 中找到数据库 aa1xxxx 所对应的条目。没有找到具有该名称的条目...
  19. 微信小程序身份证扫描OCR(信息自动带入)
  20. 一文读懂图像数据的标准化与归一化

热门文章

  1. 360无线安全负责人:如何对入侵做到未卜先知
  2. vue入门:(事件处理)
  3. 实时语音识别选型的初步调研
  4. java 向上取整方法 Math.ceil() 用法、源码分析
  5. switch java 用法_switch 用法解读
  6. MAD,现代安卓开发技术:Android 领域开发方式的重大变革~
  7. 特朗普的推特:谁还不是个“快乐源泉”了? | Alfred数据室
  8. 【UE4】界面打开关闭异常闪退
  9. qtextedit 替换_QTextEdit
  10. html鼠标悬停填充表格,鼠标移到表格上时,鼠标所在行放大高亮显示【实例】...