题目链接

题目描述

image.png
image.png
image.png

思路:暴力DFS

https://leetcode-cn.com/problems/shopping-offers/solution/tong-ge-lai-shua-ti-la-yi-ti-liang-jie-b-2ccb/
首先,观察本题给定的数据范围非常小,我们可以先尝试使用暴力求解,使用暴力的突破口是我们需要的物品数量,每当递归一次,我们就把需要的物品数量减少一次,当然,我们的每一次递归中有两个分支,即不使用礼包和使用一个礼包。

请看代码,加了详细注释:

  1. class Solution {
  2. int n;
  3. public int shoppingOffers(List<Integer> price,
  4. List<List<Integer>> special,
  5. List<Integer> needs) {
  6. this.n = price.size();
  7. return dfs(price, special, needs);
  8. }
  9. private int dfs(List<Integer> price,
  10. List<List<Integer>> special,
  11. List<Integer> needs) {
  12. // 不使用大礼包的情况下
  13. int ans = 0;
  14. for (int i = 0; i < n; i++) {
  15. ans += needs.get(i) * price.get(i);
  16. }
  17. // 使用大礼包的情况下
  18. for (List<Integer> s : special) {
  19. // 大礼包可以购买无限次
  20. // 这里相当于深拷贝一个新的List出来,所以,下面不用恢复状态了
  21. List<Integer> curr = new ArrayList<>(needs);
  22. boolean flag = false;
  23. for (int i = 0; i < n; i++) {
  24. // 判断是否超出数量限制
  25. if (curr.get(i) - s.get(i) < 0) {
  26. flag = true;
  27. break;
  28. }
  29. curr.set(i, curr.get(i) - s.get(i));
  30. }
  31. // 未超出数量限制
  32. if (!flag) {
  33. // 选定这个礼包,并继续递归
  34. ans = Math.min(ans, s.get(n) + dfs(price, special, curr));
  35. }
  36. }
  37. return ans;
  38. }
  39. }

思路二:记忆化搜索

其实,仔细分析上面的暴力解法,对于每一种需求数量,会出现重复计算,所以,我们可以加上记忆化的逻辑,遇到同一种状态,可以直接返回,提高效率。

  1. class Solution {
  2. int n;
  3. public int shoppingOffers(List<Integer> price,
  4. List<List<Integer>> special,
  5. List<Integer> needs) {
  6. this.n = price.size();
  7. Map<List<Integer>, Integer> cache = new HashMap<>();
  8. return dfs(price, special, needs, cache);
  9. }
  10. private int dfs(List<Integer> price,
  11. List<List<Integer>> special,
  12. List<Integer> needs,
  13. Map<List<Integer>, Integer> cache) {
  14. if (cache.containsKey(needs)) {
  15. return cache.get(needs);
  16. }
  17. // 不使用大礼包的情况下
  18. int ans = 0;
  19. for (int i = 0; i < n; i++) {
  20. ans += needs.get(i) * price.get(i);
  21. }
  22. // 使用大礼包的情况下
  23. for (List<Integer> s : special) {
  24. // 大礼包可以购买无限次
  25. List<Integer> curr = new ArrayList<>(needs);
  26. boolean flag = false;
  27. for (int i = 0; i < n; i++) {
  28. // 判断是否超出数量限制
  29. if (curr.get(i) - s.get(i) < 0) {
  30. flag = true;
  31. break;
  32. }
  33. curr.set(i, curr.get(i) - s.get(i));
  34. }
  35. // 未超出数量限制
  36. if (!flag) {
  37. ans = Math.min(ans, s.get(n) + dfs(price, special, curr, cache));
  38. }
  39. }
  40. cache.put(needs, ans);
  41. return ans;
  42. }
  43. }

反思

我是废物