首页 > 其他分享 >leetcode 1774. 最接近目标价格的甜点成本

leetcode 1774. 最接近目标价格的甜点成本

时间:2022-12-05 00:22:44浏览次数:63  
标签:1774 target 甜点 baseCosts 基料 textit toppingCosts 配料 leetcode

1774. 最接近目标价格的甜点成本

难度中等

你打算做甜点,现在需要购买配料。目前共有 n 种冰激凌基料和 m 种配料可供选购。而制作甜点需要遵循以下几条规则:

  • 必须选择 一种 冰激凌基料。
  • 可以添加 一种或多种 配料,也可以不添加任何配料。
  • 每种类型的配料 最多两份 。

给你以下三个输入:

  • baseCosts ,一个长度为 n 的整数数组,其中每个 baseCosts[i] 表示第 i 种冰激凌基料的价格。
  • toppingCosts,一个长度为 m 的整数数组,其中每个 toppingCosts[i] 表示 一份 第 i 种冰激凌配料的价格。
  • target ,一个整数,表示你制作甜点的目标价格。

你希望自己做的甜点总成本尽可能接近目标价格 target 。

返回最接近 target 的甜点成本。如果有多种方案,返回 成本相对较低 的一种。

 

示例 1:

输入:baseCosts = [1,7], toppingCosts = [3,4], target = 10
输出:10
解释:考虑下面的方案组合(所有下标均从 0 开始):
- 选择 1 号基料:成本 7
- 选择 1 份 0 号配料:成本 1 x 3 = 3
- 选择 0 份 1 号配料:成本 0 x 4 = 0
总成本:7 + 3 + 0 = 10 。

示例 2:

输入:baseCosts = [2,3], toppingCosts = [4,5,100], target = 18
输出:17
解释:考虑下面的方案组合(所有下标均从 0 开始):
- 选择 1 号基料:成本 3
- 选择 1 份 0 号配料:成本 1 x 4 = 4
- 选择 2 份 1 号配料:成本 2 x 5 = 10
- 选择 0 份 2 号配料:成本 0 x 100 = 0
总成本:3 + 4 + 10 + 0 = 17 。不存在总成本为 18 的甜点制作方案。

示例 3:

输入:baseCosts = [3,10], toppingCosts = [2,5], target = 9
输出:8
解释:可以制作总成本为 8 和 10 的甜点。返回 8 ,因为这是成本更低的方案。

示例 4:

输入:baseCosts = [10], toppingCosts = [1], target = 1
输出:10
解释:注意,你可以选择不添加任何配料,但你必须选择一种基料。

 

提示:

  • n == baseCosts.length
  • m == toppingCosts.length
  • 1 <= n, m <= 10
  • 1 <= baseCosts[i], toppingCosts[i] <= 104
  • 1 <= target <= 104

解题思路:

 #必须选一种基料,所以选代价最小的基料
# 辅料可以不选、选一个,选两个,可用dp或者回溯枚举

方法一:回溯
思路与算法

首先题目给出长度分别为 nn 的冰淇淋基料数组 \textit{baseCosts}baseCosts 和长度为 mm 的配料数组 \textit{toppingCosts}toppingCosts,其中 \textit{baseCosts}[i]baseCosts[i] 表示第 ii 种冰淇淋基料的价格,\textit{toppingCosts}[j]toppingCosts[j] 表示一份第 jj 种冰淇淋配料的价格,以及一个整数 \textit{target}target 表示我们需要制作甜点的目标价格。现在在制作甜品上我们需要遵守以下三条规则:

必须选择一种冰淇淋基料;
可以添加一种或多种配料,也可以不添加任何配料;
每种配料最多两份。
我们希望做的甜点总成本尽可能接近目标价格 \textit{target}target,那么我们现在按照规则对于每一种冰淇淋基料用回溯的方式来针对它进行甜品制作。又因为每一种配料都是正整数,所以在回溯的过程中总开销只能只增不减,当回溯过程中当前开销大于目标价格 \textit{target}target 后,继续往下搜索只能使开销与 \textit{target}target 的差值更大,所以如果此时差值已经大于等于我们已有最优方案的差值,我们可以停止继续往下搜索,及时回溯。

class Solution:
    def closestCost(self, baseCosts: List[int], toppingCosts: List[int], target: int) -> int:
        ans = min(baseCosts)
        def dfs(p: int, cur_cost: int) -> None:
            nonlocal ans
            if abs(ans - target) < cur_cost - target:
                return
            if abs(ans - target) >= abs(cur_cost - target):
                if abs(ans - target) > abs(cur_cost - target):
                    ans = cur_cost
                else:
                    ans = min(ans, cur_cost)
            if p == len(toppingCosts):
                return
            dfs(p + 1, cur_cost + toppingCosts[p] * 2)
            dfs(p + 1, cur_cost + toppingCosts[p])
            dfs(p + 1, cur_cost)
        for c in baseCosts:
            dfs(0, c)
        return ans

 

 

方法二:动态规划
思路与算法

我们可以将问题转化为对于某一个开销是否存在甜品制作方案问题,然后我们选择与目标价格最接近的合法甜品制作方案即可,那么问题就转化为了「01 背包」问题(关于「01 背包」的概念可见 百度百科)。这样我们就可以把问题求解从指数级别降到多项式级别了。对于「01 背包」的求解我们可以用「动态规划」来解决。

设最小的基料开销为 xx。若 x \ge \textit{target}x≥target,则无论我们是否添加配料都不会使甜品制作的开销与目标价格 \textit{target}target 的距离缩小,所以此时直接返回此最小的基料开销即可。当最小的基料开销小于 \textit{target}target 时,我们可以对超过 \textit{target}target 的制作开销方案只保存其最小的一份即可,并可以初始化为 2 \times \textit{target} - x2×target−x,因为大于该开销的方案与目标价格 \textit{target}target 的距离一定大于仅选最小基料的情况,所以一定不会是最优解。将背包的容量 \textit{MAXC}MAXC 设置为 \textit{target}target。然后我们按「01 背包」的方法来依次枚举配料来进行放置。

我们设 \textit{can}[i]can[i] 表示对于甜品制作开销为 ii 是否存在合法方案,如果存在则其等于 \text{true}true,否则为 \text{false}false,初始为 \text{false}false。因为单独选择一种基料的情况是合法的,所以我们对 \textit{can}can 进行初始化:

\textit{can}[x] = \text{true}, \forall x \in \textit{baseCosts} \And x < \textit{MAXC}
can[x]=true,∀x∈baseCosts&x<MAXC

然后我们按「01 背包」的方法来依次枚举配料来进行放置,因为每种配料我们最多只能选两份,所以我们可以直接将每种配料变为两个,然后对于两个配料都进行放置即可。因为任意一个合法方案加上一份配料一定也为合法制作方案。所以当要放置的配料开销为 yy 时,对于开销为 c, c > yc,c>y 的转移方程为:

\textit{can}[c] = \textit{can}[c - y] ~|~ \textit{can}[c], c > y
can[c]=can[c−y] ∣ can[c],c>y

因为每一个状态的求解只和前面的状态有关,所以我们可以从后往前来更新每一个状态。然后当配料全部放置后,我们可以从目标价格 \textit{target}target 往左搜索找到最接近 \textit{target}target 的合法方案并与大于 \textit{target}target 的方案做比较返回与 \textit{target}target 更接近的方案即可。

 

class Solution:
    def closestCost(self, baseCosts: List[int], toppingCosts: List[int], target: int) -> int:
        x = min(baseCosts)
        if x > target:
            return x
        can = [False] * (target + 1)
        ans = 2 * target - x
        for c in baseCosts:
            if c <= target:
                can[c] = True
            else:
                ans = min(ans, c). #必须选一种基料,所以选代价最小的基料
# 辅料可以不选、选一个,选两个,可用dp或者回溯枚举 for c in toppingCosts: for count in range(2): for i in range(target, 0, -1): if can[i] and i + c > target: ans = min(ans, i + c) if i - c > 0 and not can[i]: can[i] = can[i - c] for i in range(ans - target + 1): if can[target - i]: return target - i return ans

 


标签:1774,target,甜点,baseCosts,基料,textit,toppingCosts,配料,leetcode
From: https://www.cnblogs.com/xianbin7/p/16951288.html

相关文章

  • leetcode 101. 对称二叉树 js实现
    给你一个二叉树的根节点 root ,检查它是否轴对称。示例1:输入:root=[1,2,2,3,4,4,3]输出:true示例2:输入:root=[1,2,2,null,3,null,3]输出:false 提示:树......
  • leetcode 6256. 将节点分成尽可能多的组 二分图判定+bfs+并查集
    6256.将节点分成尽可能多的组难度困难7收藏分享切换为英文接收动态反馈给你一个正整数 n ,表示一个 无向 图中的节点数目,节点编号从 1 到 n 。同时给你一个......
  • #yyds干货盘点# LeetCode程序员面试金典:零矩阵
    题目:编写一种算法,若M×N矩阵中某个元素为0,则将其所在的行与列清零。 示例1:输入:[ [1,1,1], [1,0,1], [1,1,1]]输出:[ [1,0,1], [0,0,0], [1,0,1]]示例2:输入:[......
  • 力扣 leetcode 209. 长度最小的子数组
    问题描述给定一个含有n个正整数的数组和一个正整数target。找出该数组中满足其和≥target的长度最小的连续子数组[numsl,numsl+1,...,numsr-1,numsr],并返......
  • LeetCode刷题记录.Day31
    二叉树的层序遍历递归法classSolution{public:voidorder(TreeNode*cur,vector<vector<int>>&result,intdepth){if(cur==nullptr)retur......
  • Leetcode刷题第五周
    二叉树:种类:满二叉树、完全二叉树、二叉搜索树、平衡二叉搜索树存储方式:链式存储、线式存储(顺序存储)二叉数遍历:深度优先搜索(前序、中序、后序):使用递归实现(实际用栈来实现......
  • LeetCode:NO.142环形链表Ⅱ
    题目描述给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表......
  • 力扣 leetcode 547. 省份数量
    问题描述有n个城市,其中一些彼此相连,另一些没有相连。如果城市a与城市b直接相连,且城市b与城市c直接相连,那么城市a与城市c间接相连。省份是一组直接或间接......
  • 排序链表 重排链表 最接近目标价格的甜点成本 环形链表
    148.排序链表弄到数组里,数组排序,再弄个新链表Listlist=newArrayList<>();ListNodepre=head;while(pre!=null){list.add(pre.val);pre=pre.next;}int[......
  • 力扣 leetcode 200. 岛屿数量
    问题描述给你一个由'1'(陆地)和'0'(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此......