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 price, List> special, List needs) {
    return go(price, special, needs, new HashMap<>());
}
int go(List price, List> special, List n, Map 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 s : special) {
        boolean ok = true; List 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, int> memo;
int go(vector& price, vector>& special, vector 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 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& price, vector>& special, vector& needs) { return go(price, special, needs); }
Time: O(states·offers) Space: O(states)