Shopping Offers

medium dp backtracking

Problem

Each item has a price; special offers bundle some items at a discount. Buy exactly the needed quantities at minimum cost.

Inputprice=[2,5] special=[[3,0,5],[1,2,10]] needs=[3,2]
Output14
Two offer 2 + one item 0 = 10 + 2·2 = 14.

def shopping_offers(price, special, needs):
    from functools import lru_cache
    @lru_cache(None)
    def go(n):
        cost = sum(p*q for p, q in zip(price, n))
        for s in special:
            if all(s[i] <= n[i] for i in range(len(n))):
                cost = min(cost, s[-1] + go(tuple(n[i] - s[i] for i in range(len(n)))))
        return cost
    return go(tuple(needs))
function shoppingOffers(price, special, needs) {
  const memo = new Map();
  function go(n) {
    const k = n.join(',');
    if (memo.has(k)) return memo.get(k);
    let cost = n.reduce((s, q, i) => s + q * price[i], 0);
    for (const s of special) {
      if (s.slice(0, n.length).every((c, i) => c <= n[i]))
        cost = Math.min(cost, s[s.length-1] + go(n.map((q, i) => q - s[i])));
    }
    memo.set(k, cost); return cost;
  }
  return go(needs);
}
int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {
    return go(price, special, needs, new HashMap<>());
}
int go(List<Integer> price, List<List<Integer>> special, List<Integer> n, Map<String, Integer> memo) {
    String k = n.toString(); if (memo.containsKey(k)) return memo.get(k);
    int cost = 0; for (int i = 0; i < n.size(); i++) cost += n.get(i) * price.get(i);
    for (List<Integer> s : special) {
        boolean ok = true; List<Integer> r = new ArrayList<>(n);
        for (int i = 0; i < n.size(); i++) { if (s.get(i) > n.get(i)) { ok = false; break; } r.set(i, n.get(i) - s.get(i)); }
        if (ok) cost = Math.min(cost, s.get(s.size()-1) + go(price, special, r, memo));
    }
    memo.put(k, cost); return cost;
}
map<vector<int>, int> memo;
int go(vector<int>& price, vector<vector<int>>& special, vector<int> n) {
    if (memo.count(n)) return memo[n];
    int cost = 0; for (int i = 0; i < (int)n.size(); i++) cost += n[i] * price[i];
    for (auto& s : special) {
        bool ok = true; vector<int> r = n;
        for (int i = 0; i < (int)n.size(); i++) { if (s[i] > n[i]) { ok = false; break; } r[i] -= s[i]; }
        if (ok) cost = min(cost, s.back() + go(price, special, r));
    }
    return memo[n] = cost;
}
int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) { return go(price, special, needs); }
Time: O(states·offers) Space: O(states)