638. 大礼包

from functools import lru_cache
class Solution:
    def shoppingOffers(self, price: List[int], special: List[List[int]], needs: List[int]) -> int:
        # 递归所有可能的优惠组合
        @lru_cache(None)
        def dfs(needs):             
            res = sum(need*p for need, p in zip(needs, price))
            for s in special:
                # 更新 needs,减去礼包能够提供的数量
                # 过滤掉负数(礼包超过需要购买数量),长度会减小。
                new = [need - s for need, s in zip(needs, sp) if need >= s]

                if len(new) == n:                   
                    res = min(res, dfs(tuple(new)) + sp[-1])
                    # res = min(res, dfs(new) + s[-1])
            return res

        n = len(needs)
        # 过滤无效的礼包
        special = [sp for sp in special if sum(price[i]*sp[i] for i in range(n)) > sp[-1]]

        # 使用tuple,用lru加速
        return dfs(tuple(needs))
        # return dfs(needs)
# class Solution:
#     def shoppingOffers(self, price: List[int], special: List[List[int]], needs: List[int]) -> int:
#         res = sum(p * n for p, n in zip(price, needs))
        
#         return min([res] + [sp[-1] + self.shoppingOffers(price, special, [n - c for n, c in zip(needs, sp[:-1])]) for sp in special if not any(n < o for n, o in zip(needs, sp))])

class Solution:
    def shoppingOffers(self, price: List[int], special: List[List[int]], needs: List[int]) -> int:
        @cache
        def dfs(needs: tuple) -> int: 
            return min([sum(p * n for p, n in zip(price, needs))] + [sp[-1] + dfs(tuple(n - c for n, c in zip(needs, sp[:-1]))) for sp in special if not any(n < o for n, o in zip(needs, sp))])
            
        return dfs(tuple(needs))