LeetCode:638. Shopping Offers - Python

189 篇文章 3 订阅
151 篇文章 2 订阅

问题描述:

638. 大礼包

在LeetCode商店中, 有许多在售的物品。

然而,也有一些大礼包,每个大礼包以优惠的价格捆绑销售一组物品。

现给定每个物品的价格,每个大礼包包含物品的清单,以及待购物品清单。请输出确切完成待购清单的最低花费

每个大礼包的由一个数组中的一组数据描述,最后一个数字代表大礼包的价格,其他数字分别表示内含的其他种类物品的数量。

任意大礼包可无限次购买

示例 1:

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

说明:

  • 最多6种物品, 100种大礼包
  • 每种物品,你最多只需要购买6个
  • 你不可以购买超出待购清单的物品,即使更便宜

问题分析:

  这个题目,猛地一看,感觉无法下手,其实,仔细想想,一个深度优先搜索,是可以解决的。现在先明确题目中的一个说明,就是你不可以购买超出待购清单的物品,即使更便宜,这个很重要,可以进行减枝,加快计算。
   在这里使用记忆化搜索(Search Memoization)方法。
  (1)令 dp[cur] = val,表示,在我们需要的商品数量为cur时,的最小花费为val。例如dp[(1,2,1)] = val,表示我们需要的商品数cur = [1,2,1] 时的最小花费为val,其中dp可以使用字典数据类型。
   (2)从上向下,进行递归,如下草图(黑色线表示下递归,红色虚线表示回溯底层最优解),在最原始的状态上,遍历每一个礼包,获取最优值,每个礼包继续下递归,完成深度优先搜索。在这个过程中,每次遍历的初始化状态就是,不使用礼包时的价格。所以递推方程为:
      dp[cur] = min(val, dp.get(tmp, dfs(tmp)) + spec[-1])
tmpcur使用了某一个礼包之后的需要数, spec[-1] 对应这当前礼包的价格。在同一层上遍历一边,获取最小的那个值。
这里写图片描述

Python3实现:

# @Time   :2018/7/29
# @Author :LiuYinxing


class Solution:
    def shoppingOffers(self, price, special, needs):

        dp = {}  # 初始化,dp,用于保存每个状态的最优解

        def dfs(cur):
            val = sum(cur[i] * price[i] for i in range(len(needs)))  # 不用礼包时的价格
            for spec in special:
                tmp = [cur[j] - spec[j] for j in range(len(needs))]
                if min(tmp) >= 0:  # 过滤掉,礼包里面的商品多于需求的,礼包, 其中这个一步也相当于减枝
                    val = min(val, dp.get(tuple(tmp), dfs(tmp)) + spec[-1])  # 循环--递归--获取最优解
            dp[tuple(cur)] = val
            return val
        return dfs(needs)


if __name__ == '__main__':
    solu = Solution()
    price, special, needs = [2, 3, 4], [[1, 1, 0, 4], [2, 2, 1, 9]], [1, 2, 1]
    print(solu.shoppingOffers(price, special, needs))

知识补充:

(1)Python 字典数据结构中的,get() 函数返回指定键的值,如果值不在字典中返回默认值。
dict.get(key, default=None) 其中,

  • key – 字典中要查找的键。
  • default – 如果指定键的值不存在时,返回该默认值值。

(2)记忆化搜索(Search Memoization)
算法上依然是搜索的流程,但是搜索到的一些解用动态规划的那种思想和模式作一些保存。一般说来,动态规划总要遍历所有的状态,而搜索可以排除一些无效状态。更重要的是搜索还可以剪枝,可能剪去大量不必要的状态,因此在空间开销上往往比动态规划要低很多。记忆化算法在求解的时候还是按着自顶向下的顺序,但是每求解一个状态,就将它的解保存下来,以后再次遇到这个状态的时候,就不必重新求解了。这种方法综合了搜索和动态规划两方面的优点,因而还是很有实用价值的[1]。

参考链接:

[1] : blog.csdn.net/ilecy/article/details/50867212
欢迎指正哦。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
def big_countries(world: pd.DataFrame) -> pd.DataFrame是一个在pandas中定义的函数,它的参数是一个名为world的DataFrame。该函数的目的是过滤出符合条件的国家,并返回一个新的DataFrame,包含'name'、'population'和'area'这三列的数据。通过使用条件判断,将满足条件的行筛选出来,然后再选择所需的列返回。具体的实现方法有两种,一种是使用pandas写法,另一种是使用行过滤方法。在这两种方法中,都使用了与运算符(|)和比较运算符(>=)来对DataFrame进行条件判断,以筛选出符合条件的行。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [【Leetcode 30天Pandas挑战】学习记录 上](https://blog.csdn.net/cwtnice/article/details/132065786)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Pandas【条件筛选】](https://blog.csdn.net/Henry_Zhao10/article/details/132050959)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值