[LeetCode] 638. Shopping Offers 购物优惠

 

In LeetCode Store, there are n items to sell. Each item has a price. However, there are some special offers, and a special offer consists of one or more different kinds of items with a sale price.

You are given an integer array price where price[i] is the price of the ith item, and an integer array needs where needs[i] is the number of pieces of the ith item you want to buy.

You are also given an array special where special[i] is of size n + 1 where special[i][j] is the number of pieces of the jth item in the ith offer and special[i][n] (i.e., the last integer in the array) is the price of the ith offer.

Return the lowest price you have to pay for exactly certain items as given, where you could make optimal use of the special offers. You are not allowed to buy more items than you want, even if that would lower the overall price. You could use any of the special offers as many times as you want.

 

Example 1:

Input: price = [2,5], special = [[3,0,5],[1,2,10]], needs = [3,2]
Output: 14
Explanation: There are two kinds of items, A and B. Their prices are $2 and $5 respectively. 
In special offer 1, you can pay $5 for 3A and 0B
In special offer 2, you can pay $10 for 1A and 2B. 
You need to buy 3A and 2B, so you may pay $10 for 1A and 2B (special offer #2), and $4 for 2A.

Example 2:

Input: price = [2,3,4], special = [[1,1,0,4],[2,2,1,9]], needs = [1,2,1]
Output: 11
Explanation: The price of A is $2, and $3 for B, $4 for C. 
You may pay $4 for 1A and 1B, and $9 for 2A ,2B and 1C. 
You need to buy 1A ,2B and 1C, so you may pay $4 for 1A and 1B (special offer #1), and $3 for 1B, $4 for 1C. 
You cannot add more items, though only $9 for 2A ,2B and 1C.

 

Constraints:

  • n == price.length == needs.length
  • 1 <= n <= 6
  • 0 <= price[i], needs[i] <= 10
  • 1 <= special.length <= 100
  • special[i].length == n + 1
  • 0 <= special[i][j] <= 50

 

这道题说有一些商品,各自有不同的价格,然后给了一些优惠券,可以在优惠的价格买各种商品若干个,要求每个商品要买特定的个数,问使用优惠券能少花多少钱,注意优惠券可以重复使用,而且商品不能多买。那么可以先求出不使用任何商品需要花的钱数作为结果 res 的初始值,然后遍历每一个 coupon,对于每个遍历到的 coupon,遍历每一个商品,如果某个商品需要的个数小于 coupon 中提供的个数,说明当前 coupon 不可用,直接 break 掉循环。如果顺利遍历完了的话,表明该 coupon 可用,可以更新结果 res,对剩余的 needs 调用递归并且加上使用该 coupon 需要付的钱数。这样的方法最开始是 work 的,后来 OJ 收紧的时间,现在必须要建立记忆数组来去掉重复的计算,从而优化时间效率。这里建立一个需求数组和最小价格之间的映射,为了使用 HashMap,需要将 needs 数组 encode 成为一个字符串,然后就可以在常数时间内查找了,参见代码如下:

 

class Solution {
public:
    int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
        unordered_map<string, int> m;
        return dfs(price, special, needs, m);
    }
    int dfs(vector<int>& price, vector<vector<int>>& special, vector<int>& needs, unordered_map<string, int>& m) {
        string needStr;
        for (int need : needs) needStr += to_string(need);
        if (m.count(needStr)) return m[needStr];
        int res = 0, i = 0, n = needs.size();
        for (int i = 0; i < n; ++i) {
            res += needs[i] * price[i];
        }
        for (auto &offer : special) {
            vector<int> t;
            for (i = 0; i < n; ++i) {
                int diff = needs[i] - offer[i];
                if (diff < 0) break;
                t.push_back(diff);
            }
            if (i == n) res = min(res, offer[i] + dfs(price, special, t, m));
        }
        return m[needStr] = res;
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/638

 

参考资料:

https://leetcode.com/problems/shopping-offers/

https://leetcode.com/problems/shopping-offers/discuss/1381625/C%2B%2B-or-Easy-to-Understand-or-Recursion-%2B-Memoization

 

LeetCode All in One 题目讲解汇总(持续更新中...) 

posted @ 2017-07-31 10:05  Grandyang  阅读(7688)  评论(2编辑  收藏  举报
Fork me on GitHub