You would like to make dessert and are preparing to buy the ingredients. You have
n
ice cream base flavors andm
types of toppings to choose from. You must follow these rules when making your dessert:
- There must be exactly one ice cream base.
- You can add one or more types of topping or have no toppings at all.
- There are at most two of each type of topping.
You are given three inputs:
baseCosts
, an integer array of lengthn
, where eachbaseCosts[i]
represents the price of theith
ice cream base flavor.toppingCosts
, an integer array of lengthm
, where eachtoppingCosts[i]
is the price of one of theith
topping.target
, an integer representing your target price for dessert.You want to make a dessert with a total cost as close to
target
as possible.Return the closest possible cost of the dessert to
target
. If there are multiple, return the lower one.
你打算做甜点,现在需要购买配料。目前共有
n
种冰激凌基料和m
种配料可供选购。而制作甜点需要遵循以下几条规则:
- 必须选择 一种 冰激凌基料。
- 可以添加 一种或多种 配料,也可以不添加任何配料。
- 每种类型的配料 最多两份 。
给你以下三个输入:
baseCosts
,一个长度为n
的整数数组,其中每个baseCosts[i]
表示第i
种冰激凌基料的价格。toppingCosts
,一个长度为m
的整数数组,其中每个toppingCosts[i]
表示 一份 第i
种冰激凌配料的价格。target
,一个整数,表示你制作甜点的目标价格。你希望自己做的甜点总成本尽可能接近目标价格
target
。返回最接近
target
的甜点成本。如果有多种方案,返回 成本相对较低 的一种。
今天是苦命加班人呀,忙里做题,哈希表的方法明天再写了,继续写本子了=(
哈希表搞定,赶今天的每日一题了(12/05)
首先,明确下题意,可以转化为有限制条件的x个数之和,我们需要返回最接近target的x数之和。
baseCosts
必须选取一个,配料toppingCosts
可以选取数量不限,但是每种配料最多选择两个。target
,那么该值即为最接近target
的值,直接返回该值即可。target
的x数之和。这里的动态规划,感觉更像是用布尔类型的数组代替了哈希表。思路:对于每种底料,回溯查找其所有可能的配料组合,返回最接近target
的值
回溯三部曲
回溯函数模板返回值以及参数
toppingCosts
(底料数组)i
(当前选择的元素)curCost
(当前的总和)target
(目标和)res
(最接近target的x数之和)回溯函数终止条件
curCost
已经不可能再对最终结果有影响时,即
c
u
r
C
o
s
t
>
t
a
r
g
e
t
curCost > target
curCost>target 并且res
更加接近target
(剪枝1)toppingCosts
的长度时(终止条件)回溯搜索的遍历过程
target
,若是则更新res
res
是否是最接近target
的值或者所有配料已经遍历完,是则结束循环class Solution {
int res;
public int closestCost(int[] baseCosts, int[] toppingCosts, int target) {
int minBase = baseCosts[0];
for (int baseCost : baseCosts){
minBase = Math.min(minBase,baseCost);
}
if (minBase >= target){
return minBase;
}
res = minBase;
for (int baseCost : baseCosts){
backtracking(toppingCosts, 0, baseCost, target);
}
return res;
}
public void backtracking(int[] toppingCosts, int i, int curCost, int target){
if (curCost - target > Math.abs(res - target)){
return;
}
if (Math.abs(curCost - target) < Math.abs(res - target)){
res = curCost;
}else if (Math.abs(curCost - target) == Math.abs(res - target)){
res = Math.min(curCost, res);
}
if (i == toppingCosts.length || res == target){
return;
}
backtracking(toppingCosts, i + 1, curCost, target);
backtracking(toppingCosts, i + 1, curCost + toppingCosts[i], target);
backtracking(toppingCosts, i + 1, curCost + toppingCosts[i] * 2, target);
}
}
baseCosts
的长度,
m
m
m为配料toppingCosts
的长度思路:由于最终答案可能大于target也可能小于target,可能的范围为
0
0
0至
2
∗
t
a
r
g
e
t
2*target
2∗target。因此,可以使用一个布尔类型的数组flag
,数组长度为2*target+1
,记录可能出现的x数之和,最后返回最接近target的x数之和即可
实现:
target
,直接返回targettarget
,直接返回冰淇淋底料的最小值baseCosts
,将flag[baseCost]
设置为true
flag
数组target
为中心,返回最接近target
的x数之和class Solution {
int res;
public int closestCost(int[] baseCosts, int[] toppingCosts, int target) {
int minBase = baseCosts[0];
boolean[] flag = new boolean[target * 2 + 1];
for (int baseCost : baseCosts){
if (baseCost == target){
return target;
}else if (baseCost < 2 * target){
flag[baseCost] = true;
}
minBase = Math.min(minBase,baseCost);
}
if (minBase >= target){
return minBase;
}
for (int toppingCost : toppingCosts){
for (int count = 0; count <= 1; count++){
for (int i = 2 * target; i >= 0; i--){
if (i - toppingCost >= 0){
flag[i] = flag[i] | flag[i - toppingCost];
}
}
}
}
for (int i = 0; i <= target; i++){
if (flag[target - i]){
return target - i;
}else if (flag[target + i]){
return target + i;
}
}
return 0;
}
}
baseCosts
的长度,
m
m
m为配料toppingCosts
的长度优化:单独使用变量记录大于target时最小的x数之和 r e s res res,优化空间复杂度和时间复杂度,最后只需在 [ t a r g e t − ( r e s − t a r g e t ) , t a r g e t ] [target-(res-target),target] [target−(res−target),target]范围内查找是否存在更接近 t a r g e t target target的x数之和即可,从 t a r g e t target target开始向下查找 r e s − t a r g e t res-target res−target个值。
class Solution {
public int closestCost(int[] baseCosts, int[] toppingCosts, int target) {
int minBase = baseCosts[0];
boolean[] flag = new boolean[target + 1];
int res = Integer.MAX_VALUE;
for (int baseCost : baseCosts){
if (baseCost == target){
return target;
}else if (baseCost > target){
res = Math.min(res,baseCost);
}else if (baseCost < target){
flag[baseCost] = true;
}
minBase = Math.min(minBase,baseCost);
}
if (minBase >= target){
return minBase;
}
res = Math.min(res,2 * target - minBase);
for (int toppingCost : toppingCosts){
for (int count = 0; count <= 1; count++){
for (int i = target; i >= 0; i--){
if (flag[i] && i + toppingCost > target){
res = Math.min(res, i + toppingCost);
}
if (i - toppingCost >= 0){
flag[i] = flag[i] | flag[i - toppingCost];
}
}
}
}
for (int i = 0 ; i <= res - target; i++){
if (flag[target - i]){
return target - i;
}
}
return res;
}
}