[LeetCode638. shopping-offers] | 刷题打卡

226 阅读1分钟

一、题目描述:

leetcode-cn.com/problems/sh…
现给定每个物品的价格,每个大礼包包含物品的清单,以及待购物品清单。请输出确切完成待购清单的最低花费。
每个大礼包的由一个数组中的一组数据描述,最后一个数字代表大礼包的价格,其他数字分别表示内含的其他种类物品的数量。
任意大礼包可无限次购买。

****示例 1:

输入: [2,3,4], [[1,1,0,4],[2,2,1,9]], [1,2,1]
输出: 11
解释: 
ABC的价格分别为¥2,¥3,¥4.
你可以用¥4购买1A1B,也可以用¥9购买2A2B1C。
你需要买1A2B1C,所以你付了¥4买了1A1B(大礼包1),以及¥3购买1B, ¥4购买1C。
你不可以购买超出待购清单的物品,尽管购买大礼包2更加便宜。

二、思路分析:

  • 递归 + dfs
  • 递归函数定义为当前还剩余需要买的物品最少花的钱.
  • 首选从购买礼包套餐,若是购买礼包套餐后,剩余还需要购买的物品不为负数,则这个方案是可行的继续递归购买剩下的物品.

三、AC 代码:

/**
 * @param {number[]} price
 * @param {number[][]} special
 * @param {number[]} needs
 * @return {number}
 */
var shoppingOffers = function(price, special, needs) {


    function dfs(lefted){
        
        let min = getMoney(lefted)

        for(let i=0;i<special.length;i++){
            const spOffer = special[i]
            const leftedNeeds = canBuySpOffer([...lefted],spOffer)
            if(leftedNeeds.length){
                const res = dfs([...leftedNeeds])
                min = Math.min(min,res + spOffer[spOffer.length-1]) 
            }

        }
        return min

    }
    function canBuySpOffer(lefted,special){
        for(let i=0;i<lefted.length;i++){
            const diff = lefted[i] -  special[i]
            lefted[i] = diff
            if(diff<0){
                return []
            }
        }
        return lefted
    }

    function getMoney(lefted){
        let sum = 0
        for(let i=0;i<lefted.length;i++){
            sum += lefted[i] * price[i]
        }
        return sum
    }

    return dfs(needs)
};

四、总结:

  • 抓住核心,即状态转移