我是靠谱客的博主 善良墨镜,最近开发中收集的这篇文章主要介绍LeetCode:638. Shopping Offers - Python,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

问题描述:

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
欢迎指正哦。

最后

以上就是善良墨镜为你收集整理的LeetCode:638. Shopping Offers - Python的全部内容,希望文章能够帮你解决LeetCode:638. Shopping Offers - Python所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部