力扣-1774. 最接近目标价格的甜点成本
本文最后更新于:2022年12月5日 晚上
题目描述
思路及实现
深度优先搜索
对于深度优先搜索做法的话,就没什么好说的了,就是枚举所有情况然后找个最符合题意的结果返回就是了。
1 |
|
当然,为了避免没有必要的搜索,可以进行剪枝。
1 |
|
动态规划
本题属于是01背包问题的变种,在这题里边,存在一定要放入且只能放入一个的基料,还有可放入可不放入的辅料,所以可以把辅料看作是背包问题中的物品,而这个物品(辅料)是可以放两次的,在背包问题中只能放一次,而且,对于这题,需要考虑如何处理大于target
的部分,因为在01背包问题中,总物品重量是不能超过背包可承载重量的,但是这里制作成本可以超过目标制造成本,因为我们考虑的是更小的成本差距。
那就需要对于这些不同的部分进行处理了,我们来一步一步处理:
- 在这里,我们是通过01背包问题的方式来求对于一个成本
cost
,我们是否能够找到一个构造方案。最后再找到距离target
最近的可构造的成本方案。 - 题目有一个隐藏上限:即成本差距最大就是
(target - min(baseCosts))
,大于此成本差距的制作方法一定不是最优解。 - 首先对于放入的基料而言,如果处理?对于小于
target
的基料,我们就尝试往上面添加辅料,即把它们作为动态规划数组的初始状态;对于大于target
的基料,根据第1点提到过的上限,我们可以知道如果基料成本大于2 * target - min(baseCosts)
的话一定不是最优解,小于的话,就用它更新最小成本。 - 对于一种辅料最多放两份,那就每种辅料都处理两次,即进行两次循环。
- 如何添加辅料?对于一种方案
cost
,我们设i
为第i
种辅料,那么有dp[cost] = dp[cost - toppingCosts[i]] | dp[cost]
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Solution:
def closestCost(self, baseCosts: List[int], toppingCosts: List[int], target: int) -> int:
cost = min(baseCosts)
if cost >= target:
return cost
ans = 2 * target - cost
dp = [False] * (target + 1)
for c in baseCosts:
if c == target:
return c
if c > target:
ans = min(ans, c)
else:
dp[c] = True
for i in toppingCosts:
for _ in range(2):
for c in range(target, 0, -1):
if dp[c] and c + i > target: # 在这题中要额外处理一种成本方案加入辅料后大于target的情况
ans = min(ans, c + i)
if c > i: # 01背包构造成本方案
dp[c] |= dp[c - i]
for i in range(ans - target + 1):
if dp[target - i]:
return target - i
return ans
其他话(关于背包DP)
没想到吧,这么经典的题我也是第一次了解,之前只听过没写过。
首先是放一下学习的地方嗷:背包DP
这里主要是写一些我在看这个问题时候的疑惑以及解释。
第一个疑惑:错误的核心代码里边写的什么?l
从0
到W - w[i]
是啥意思啊?
还有一点需要注意的是,很容易写出这样的错误核心代码:
1
2
3
4
5
6
7
# Python Version
for i in range(1, n + 1):
l = 0
while l <= W - w[i]:
f[l + w[i]] = max(f[l] + v[i], f[l + w[i]])
l += 1
# 由 f[i][l + w[i]] = max(max(f[i - 1][l + w[i]],f[i - 1][l] + w[i]),f[i][l + w[i]]) 简化而来OK,那么要明白对于背包问题中
f[i][j]
是什么,f[i][j]
就是只放前i
(包括第i
个)个物品时,背包重量为j
的时候,背包中物品的最大价值。
那么现在能够看出,这里在求从f[i][0 + w[i]]
开始,一直到f[i][l + w[i]]
(为什么是这个数呢?因为不能超过背包可承载的重量W
),计算f[0 + w[i]]
至f[l + w[i]]
每一个重量下所能达到的最大总价值。
为什么这里错了呢?因为f[i][j]
会受到f[i][j - w[i]]
的影响,这又是什么意思呢?意思是我们并不知道f[i][j - w[i]]
这种情况下是否是在i
被塞入的时候计算得出的,如果是,而且f[i][j]
还更新为f[i][j - w[i]]
的值的话,那就相当于把这个物品塞入多次,不符合题意了。
举个例子,现在有l = 2, w[i] = 4
,那么会有:f[2 + 4] = max(f[2] + v[i], f[2 + 4])
,而对于在这之后可能出现的情况l = 6, w[i] = 4
,又有:f[6 + 4] = max(f[6] + v[i], f[6 + 4])
,而这其中出现的f[6]
,怎么保证不是我们之前在求f[2 + 4]
的时候取的f[2] + v[i]
的值呢?如果是的话,那物品i
不就相当于放入多次了吗?
所以,为了避免出现这种情况,我们选择了从l = W
开始枚举而不是从l = 0
开始。
1 |
|
这样,我们就能保证f[i][j]
总是在f[i][j - w[i]]
之前更新了。
至此,01背包问题就解决完毕了。
希望本文章能够帮到您~